blob: 98f37c0ecadba393da87c27191f20c0223d32fe9 [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
167 xmlXPathNAN = 0;
168 xmlXPathNAN /= 0;
169
170 xmlXPathPINF = 1;
171 xmlXPathPINF /= 0;
172
173 xmlXPathNINF = -1;
174 xmlXPathNINF /= 0;
175
176 initialized = 1;
177}
178
179/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000180 * *
181 * Parser Types *
182 * *
183 ************************************************************************/
184
185/*
186 * Types are private:
187 */
188
189typedef enum {
190 XPATH_OP_END=0,
191 XPATH_OP_AND,
192 XPATH_OP_OR,
193 XPATH_OP_EQUAL,
194 XPATH_OP_CMP,
195 XPATH_OP_PLUS,
196 XPATH_OP_MULT,
197 XPATH_OP_UNION,
198 XPATH_OP_ROOT,
199 XPATH_OP_NODE,
200 XPATH_OP_RESET,
201 XPATH_OP_COLLECT,
202 XPATH_OP_VALUE,
203 XPATH_OP_VARIABLE,
204 XPATH_OP_FUNCTION,
205 XPATH_OP_ARG,
206 XPATH_OP_PREDICATE,
207 XPATH_OP_SORT
208#ifdef LIBXML_XPTR_ENABLED
209 ,XPATH_OP_RANGETO
210#endif
211} xmlXPathOp;
212
213typedef enum {
214 AXIS_ANCESTOR = 1,
215 AXIS_ANCESTOR_OR_SELF,
216 AXIS_ATTRIBUTE,
217 AXIS_CHILD,
218 AXIS_DESCENDANT,
219 AXIS_DESCENDANT_OR_SELF,
220 AXIS_FOLLOWING,
221 AXIS_FOLLOWING_SIBLING,
222 AXIS_NAMESPACE,
223 AXIS_PARENT,
224 AXIS_PRECEDING,
225 AXIS_PRECEDING_SIBLING,
226 AXIS_SELF
227} xmlXPathAxisVal;
228
229typedef enum {
230 NODE_TEST_NONE = 0,
231 NODE_TEST_TYPE = 1,
232 NODE_TEST_PI = 2,
233 NODE_TEST_ALL = 3,
234 NODE_TEST_NS = 4,
235 NODE_TEST_NAME = 5
236} xmlXPathTestVal;
237
238typedef enum {
239 NODE_TYPE_NODE = 0,
240 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
241 NODE_TYPE_TEXT = XML_TEXT_NODE,
242 NODE_TYPE_PI = XML_PI_NODE
243} xmlXPathTypeVal;
244
245
246typedef struct _xmlXPathStepOp xmlXPathStepOp;
247typedef xmlXPathStepOp *xmlXPathStepOpPtr;
248struct _xmlXPathStepOp {
249 xmlXPathOp op;
250 int ch1;
251 int ch2;
252 int value;
253 int value2;
254 int value3;
255 void *value4;
256 void *value5;
257};
258
259struct _xmlXPathCompExpr {
260 int nbStep;
261 int maxStep;
262 xmlXPathStepOp *steps; /* ops for computation */
263 int last;
264};
265
266/************************************************************************
267 * *
268 * Parser Type functions *
269 * *
270 ************************************************************************/
271
272/**
273 * xmlXPathNewCompExpr:
274 *
275 * Create a new Xpath component
276 *
277 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
278 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000279static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000280xmlXPathNewCompExpr(void) {
281 xmlXPathCompExprPtr cur;
282
283 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
284 if (cur == NULL) {
285 xmlGenericError(xmlGenericErrorContext,
286 "xmlXPathNewCompExpr : malloc failed\n");
287 return(NULL);
288 }
289 memset(cur, 0, sizeof(xmlXPathCompExpr));
290 cur->maxStep = 10;
291 cur->nbStep = 0;
292 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
293 sizeof(xmlXPathStepOp));
294 if (cur->steps == NULL) {
295 xmlGenericError(xmlGenericErrorContext,
296 "xmlXPathNewCompExpr : malloc failed\n");
297 xmlFree(cur);
298 return(NULL);
299 }
300 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
301 cur->last = -1;
302 return(cur);
303}
304
305/**
306 * xmlXPathFreeCompExpr:
307 * @comp: an XPATH comp
308 *
309 * Free up the memory allocated by @comp
310 */
311void
312xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
313 xmlXPathStepOpPtr op;
314 int i;
315
316 if (comp == NULL)
317 return;
318 for (i = 0;i < comp->nbStep;i++) {
319 op = &comp->steps[i];
320 if (op->value4 != NULL) {
321 if (op->op == XPATH_OP_VALUE)
322 xmlXPathFreeObject(op->value4);
323 else
324 xmlFree(op->value4);
325 }
326 if (op->value5 != NULL)
327 xmlFree(op->value5);
328 }
329 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000330 xmlFree(comp->steps);
331 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000332 xmlFree(comp);
333}
334
335/**
336 * xmlXPathCompExprAdd:
337 * @comp: the compiled expression
338 * @ch1: first child index
339 * @ch2: second child index
340 * @op: an op
341 * @value: the first int value
342 * @value2: the second int value
343 * @value3: the third int value
344 * @value4: the first string value
345 * @value5: the second string value
346 *
347 * Add an step to an XPath Compiled Expression
348 *
349 * Returns -1 in case of failure, the index otherwise
350 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000351static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000352xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
353 xmlXPathOp op, int value,
354 int value2, int value3, void *value4, void *value5) {
355 if (comp->nbStep >= comp->maxStep) {
356 xmlXPathStepOp *real;
357
358 comp->maxStep *= 2;
359 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
360 comp->maxStep * sizeof(xmlXPathStepOp));
361 if (real == NULL) {
362 comp->maxStep /= 2;
363 xmlGenericError(xmlGenericErrorContext,
364 "xmlXPathCompExprAdd : realloc failed\n");
365 return(-1);
366 }
367 comp->steps = real;
368 }
369 comp->last = comp->nbStep;
370 comp->steps[comp->nbStep].ch1 = ch1;
371 comp->steps[comp->nbStep].ch2 = ch2;
372 comp->steps[comp->nbStep].op = op;
373 comp->steps[comp->nbStep].value = value;
374 comp->steps[comp->nbStep].value2 = value2;
375 comp->steps[comp->nbStep].value3 = value3;
376 comp->steps[comp->nbStep].value4 = value4;
377 comp->steps[comp->nbStep].value5 = value5;
378 return(comp->nbStep++);
379}
380
381#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
382 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
383 (op), (val), (val2), (val3), (val4), (val5))
384
385#define PUSH_LEAVE_EXPR(op, val, val2) \
386xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
387
388#define PUSH_UNARY_EXPR(op, ch, val, val2) \
389xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
390
391#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
392xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
393
394/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000395 * *
396 * Debugging related functions *
397 * *
398 ************************************************************************/
399
400#define TODO \
401 xmlGenericError(xmlGenericErrorContext, \
402 "Unimplemented block at %s:%d\n", \
403 __FILE__, __LINE__);
404
405#define STRANGE \
406 xmlGenericError(xmlGenericErrorContext, \
407 "Internal error at %s:%d\n", \
408 __FILE__, __LINE__);
409
410#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000411static void
412xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000413 int i;
414 char shift[100];
415
416 for (i = 0;((i < depth) && (i < 25));i++)
417 shift[2 * i] = shift[2 * i + 1] = ' ';
418 shift[2 * i] = shift[2 * i + 1] = 0;
419 if (cur == NULL) {
420 fprintf(output, shift);
421 fprintf(output, "Node is NULL !\n");
422 return;
423
424 }
425
426 if ((cur->type == XML_DOCUMENT_NODE) ||
427 (cur->type == XML_HTML_DOCUMENT_NODE)) {
428 fprintf(output, shift);
429 fprintf(output, " /\n");
430 } else if (cur->type == XML_ATTRIBUTE_NODE)
431 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
432 else
433 xmlDebugDumpOneNode(output, cur, depth);
434}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000435static void
436xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000437 xmlNodePtr tmp;
438 int i;
439 char shift[100];
440
441 for (i = 0;((i < depth) && (i < 25));i++)
442 shift[2 * i] = shift[2 * i + 1] = ' ';
443 shift[2 * i] = shift[2 * i + 1] = 0;
444 if (cur == NULL) {
445 fprintf(output, shift);
446 fprintf(output, "Node is NULL !\n");
447 return;
448
449 }
450
451 while (cur != NULL) {
452 tmp = cur;
453 cur = cur->next;
454 xmlDebugDumpOneNode(output, tmp, depth);
455 }
456}
Owen Taylor3473f882001-02-23 17:55:21 +0000457
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000458static void
459xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000460 int i;
461 char shift[100];
462
463 for (i = 0;((i < depth) && (i < 25));i++)
464 shift[2 * i] = shift[2 * i + 1] = ' ';
465 shift[2 * i] = shift[2 * i + 1] = 0;
466
467 if (cur == NULL) {
468 fprintf(output, shift);
469 fprintf(output, "NodeSet is NULL !\n");
470 return;
471
472 }
473
474 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
475 for (i = 0;i < cur->nodeNr;i++) {
476 fprintf(output, shift);
477 fprintf(output, "%d", i + 1);
478 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
479 }
480}
481
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000482static void
483xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000484 int i;
485 char shift[100];
486
487 for (i = 0;((i < depth) && (i < 25));i++)
488 shift[2 * i] = shift[2 * i + 1] = ' ';
489 shift[2 * i] = shift[2 * i + 1] = 0;
490
491 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
492 fprintf(output, shift);
493 fprintf(output, "Value Tree is NULL !\n");
494 return;
495
496 }
497
498 fprintf(output, shift);
499 fprintf(output, "%d", i + 1);
500 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
501}
Owen Taylor3473f882001-02-23 17:55:21 +0000502#if defined(LIBXML_XPTR_ENABLED)
503void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000504static void
505xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000506 int i;
507 char shift[100];
508
509 for (i = 0;((i < depth) && (i < 25));i++)
510 shift[2 * i] = shift[2 * i + 1] = ' ';
511 shift[2 * i] = shift[2 * i + 1] = 0;
512
513 if (cur == NULL) {
514 fprintf(output, shift);
515 fprintf(output, "LocationSet is NULL !\n");
516 return;
517
518 }
519
520 for (i = 0;i < cur->locNr;i++) {
521 fprintf(output, shift);
522 fprintf(output, "%d : ", i + 1);
523 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
524 }
525}
526#endif
527
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000528/**
529 * xmlXPathDebugDumpObject:
530 * @output: the FILE * to dump the output
531 * @cur: the object to inspect
532 * @depth: indentation level
533 *
534 * Dump the content of the object for debugging purposes
535 */
536void
537xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000538 int i;
539 char shift[100];
540
541 for (i = 0;((i < depth) && (i < 25));i++)
542 shift[2 * i] = shift[2 * i + 1] = ' ';
543 shift[2 * i] = shift[2 * i + 1] = 0;
544
545 fprintf(output, shift);
546
547 if (cur == NULL) {
548 fprintf(output, "Object is empty (NULL)\n");
549 return;
550 }
551 switch(cur->type) {
552 case XPATH_UNDEFINED:
553 fprintf(output, "Object is uninitialized\n");
554 break;
555 case XPATH_NODESET:
556 fprintf(output, "Object is a Node Set :\n");
557 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
558 break;
559 case XPATH_XSLT_TREE:
560 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000561 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000562 break;
563 case XPATH_BOOLEAN:
564 fprintf(output, "Object is a Boolean : ");
565 if (cur->boolval) fprintf(output, "true\n");
566 else fprintf(output, "false\n");
567 break;
568 case XPATH_NUMBER:
569 fprintf(output, "Object is a number : %0g\n", cur->floatval);
570 break;
571 case XPATH_STRING:
572 fprintf(output, "Object is a string : ");
573 xmlDebugDumpString(output, cur->stringval);
574 fprintf(output, "\n");
575 break;
576 case XPATH_POINT:
577 fprintf(output, "Object is a point : index %d in node", cur->index);
578 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
579 fprintf(output, "\n");
580 break;
581 case XPATH_RANGE:
582 if ((cur->user2 == NULL) ||
583 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
584 fprintf(output, "Object is a collapsed range :\n");
585 fprintf(output, shift);
586 if (cur->index >= 0)
587 fprintf(output, "index %d in ", cur->index);
588 fprintf(output, "node\n");
589 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
590 depth + 1);
591 } else {
592 fprintf(output, "Object is a range :\n");
593 fprintf(output, shift);
594 fprintf(output, "From ");
595 if (cur->index >= 0)
596 fprintf(output, "index %d in ", cur->index);
597 fprintf(output, "node\n");
598 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
599 depth + 1);
600 fprintf(output, shift);
601 fprintf(output, "To ");
602 if (cur->index2 >= 0)
603 fprintf(output, "index %d in ", cur->index2);
604 fprintf(output, "node\n");
605 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
606 depth + 1);
607 fprintf(output, "\n");
608 }
609 break;
610 case XPATH_LOCATIONSET:
611#if defined(LIBXML_XPTR_ENABLED)
612 fprintf(output, "Object is a Location Set:\n");
613 xmlXPathDebugDumpLocationSet(output,
614 (xmlLocationSetPtr) cur->user, depth);
615#endif
616 break;
617 case XPATH_USERS:
618 fprintf(output, "Object is user defined\n");
619 break;
620 }
621}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000622
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000623static void
624xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000625 xmlXPathStepOpPtr op, int depth) {
626 int i;
627 char shift[100];
628
629 for (i = 0;((i < depth) && (i < 25));i++)
630 shift[2 * i] = shift[2 * i + 1] = ' ';
631 shift[2 * i] = shift[2 * i + 1] = 0;
632
633 fprintf(output, shift);
634 if (op == NULL) {
635 fprintf(output, "Step is NULL\n");
636 return;
637 }
638 switch (op->op) {
639 case XPATH_OP_END:
640 fprintf(output, "END"); break;
641 case XPATH_OP_AND:
642 fprintf(output, "AND"); break;
643 case XPATH_OP_OR:
644 fprintf(output, "OR"); break;
645 case XPATH_OP_EQUAL:
646 if (op->value)
647 fprintf(output, "EQUAL =");
648 else
649 fprintf(output, "EQUAL !=");
650 break;
651 case XPATH_OP_CMP:
652 if (op->value)
653 fprintf(output, "CMP <");
654 else
655 fprintf(output, "CMP >");
656 if (!op->value2)
657 fprintf(output, "=");
658 break;
659 case XPATH_OP_PLUS:
660 if (op->value == 0)
661 fprintf(output, "PLUS -");
662 else if (op->value == 1)
663 fprintf(output, "PLUS +");
664 else if (op->value == 2)
665 fprintf(output, "PLUS unary -");
666 else if (op->value == 3)
667 fprintf(output, "PLUS unary - -");
668 break;
669 case XPATH_OP_MULT:
670 if (op->value == 0)
671 fprintf(output, "MULT *");
672 else if (op->value == 1)
673 fprintf(output, "MULT div");
674 else
675 fprintf(output, "MULT mod");
676 break;
677 case XPATH_OP_UNION:
678 fprintf(output, "UNION"); break;
679 case XPATH_OP_ROOT:
680 fprintf(output, "ROOT"); break;
681 case XPATH_OP_NODE:
682 fprintf(output, "NODE"); break;
683 case XPATH_OP_RESET:
684 fprintf(output, "RESET"); break;
685 case XPATH_OP_SORT:
686 fprintf(output, "SORT"); break;
687 case XPATH_OP_COLLECT: {
688 xmlXPathAxisVal axis = op->value;
689 xmlXPathTestVal test = op->value2;
690 xmlXPathTypeVal type = op->value3;
691 const xmlChar *prefix = op->value4;
692 const xmlChar *name = op->value5;
693
694 fprintf(output, "COLLECT ");
695 switch (axis) {
696 case AXIS_ANCESTOR:
697 fprintf(output, " 'ancestors' "); break;
698 case AXIS_ANCESTOR_OR_SELF:
699 fprintf(output, " 'ancestors-or-self' "); break;
700 case AXIS_ATTRIBUTE:
701 fprintf(output, " 'attributes' "); break;
702 case AXIS_CHILD:
703 fprintf(output, " 'child' "); break;
704 case AXIS_DESCENDANT:
705 fprintf(output, " 'descendant' "); break;
706 case AXIS_DESCENDANT_OR_SELF:
707 fprintf(output, " 'descendant-or-self' "); break;
708 case AXIS_FOLLOWING:
709 fprintf(output, " 'following' "); break;
710 case AXIS_FOLLOWING_SIBLING:
711 fprintf(output, " 'following-siblings' "); break;
712 case AXIS_NAMESPACE:
713 fprintf(output, " 'namespace' "); break;
714 case AXIS_PARENT:
715 fprintf(output, " 'parent' "); break;
716 case AXIS_PRECEDING:
717 fprintf(output, " 'preceding' "); break;
718 case AXIS_PRECEDING_SIBLING:
719 fprintf(output, " 'preceding-sibling' "); break;
720 case AXIS_SELF:
721 fprintf(output, " 'self' "); break;
722 }
723 switch (test) {
724 case NODE_TEST_NONE:
725 fprintf(output, "'none' "); break;
726 case NODE_TEST_TYPE:
727 fprintf(output, "'type' "); break;
728 case NODE_TEST_PI:
729 fprintf(output, "'PI' "); break;
730 case NODE_TEST_ALL:
731 fprintf(output, "'all' "); break;
732 case NODE_TEST_NS:
733 fprintf(output, "'namespace' "); break;
734 case NODE_TEST_NAME:
735 fprintf(output, "'name' "); break;
736 }
737 switch (type) {
738 case NODE_TYPE_NODE:
739 fprintf(output, "'node' "); break;
740 case NODE_TYPE_COMMENT:
741 fprintf(output, "'comment' "); break;
742 case NODE_TYPE_TEXT:
743 fprintf(output, "'text' "); break;
744 case NODE_TYPE_PI:
745 fprintf(output, "'PI' "); break;
746 }
747 if (prefix != NULL)
748 fprintf(output, "%s:", prefix);
749 if (name != NULL)
750 fprintf(output, "%s", name);
751 break;
752
753 }
754 case XPATH_OP_VALUE: {
755 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
756
757 fprintf(output, "ELEM ");
758 xmlXPathDebugDumpObject(output, object, 0);
759 goto finish;
760 }
761 case XPATH_OP_VARIABLE: {
762 const xmlChar *prefix = op->value5;
763 const xmlChar *name = op->value4;
764
765 if (prefix != NULL)
766 fprintf(output, "VARIABLE %s:%s", prefix, name);
767 else
768 fprintf(output, "VARIABLE %s", name);
769 break;
770 }
771 case XPATH_OP_FUNCTION: {
772 int nbargs = op->value;
773 const xmlChar *prefix = op->value5;
774 const xmlChar *name = op->value4;
775
776 if (prefix != NULL)
777 fprintf(output, "FUNCTION %s:%s(%d args)",
778 prefix, name, nbargs);
779 else
780 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
781 break;
782 }
783 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
784 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
785 default:
786 fprintf(output, "UNKNOWN %d\n", op->op); return;
787 }
788 fprintf(output, "\n");
789finish:
790 if (op->ch1 >= 0)
791 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
792 if (op->ch2 >= 0)
793 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
794}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000795
796void
797xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
798 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000799 int i;
800 char shift[100];
801
802 for (i = 0;((i < depth) && (i < 25));i++)
803 shift[2 * i] = shift[2 * i + 1] = ' ';
804 shift[2 * i] = shift[2 * i + 1] = 0;
805
806 fprintf(output, shift);
807
808 if (comp == NULL) {
809 fprintf(output, "Compiled Expression is NULL\n");
810 return;
811 }
812 fprintf(output, "Compiled Expression : %d elements\n",
813 comp->nbStep);
814 i = comp->last;
815 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
816}
Owen Taylor3473f882001-02-23 17:55:21 +0000817#endif
818
819/************************************************************************
820 * *
821 * Parser stacks related functions and macros *
822 * *
823 ************************************************************************/
824
825/*
826 * Generic function for accessing stacks in the Parser Context
827 */
828
829#define PUSH_AND_POP(type, name) \
830extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
831 if (ctxt->name##Nr >= ctxt->name##Max) { \
832 ctxt->name##Max *= 2; \
833 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
834 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
835 if (ctxt->name##Tab == NULL) { \
836 xmlGenericError(xmlGenericErrorContext, \
837 "realloc failed !\n"); \
838 return(0); \
839 } \
840 } \
841 ctxt->name##Tab[ctxt->name##Nr] = value; \
842 ctxt->name = value; \
843 return(ctxt->name##Nr++); \
844} \
845extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
846 type ret; \
847 if (ctxt->name##Nr <= 0) return(0); \
848 ctxt->name##Nr--; \
849 if (ctxt->name##Nr > 0) \
850 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
851 else \
852 ctxt->name = NULL; \
853 ret = ctxt->name##Tab[ctxt->name##Nr]; \
854 ctxt->name##Tab[ctxt->name##Nr] = 0; \
855 return(ret); \
856} \
857
858PUSH_AND_POP(xmlXPathObjectPtr, value)
859
860/*
861 * Macros for accessing the content. Those should be used only by the parser,
862 * and not exported.
863 *
864 * Dirty macros, i.e. one need to make assumption on the context to use them
865 *
866 * CUR_PTR return the current pointer to the xmlChar to be parsed.
867 * CUR returns the current xmlChar value, i.e. a 8 bit value
868 * in ISO-Latin or UTF-8.
869 * This should be used internally by the parser
870 * only to compare to ASCII values otherwise it would break when
871 * running with UTF-8 encoding.
872 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
873 * to compare on ASCII based substring.
874 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
875 * strings within the parser.
876 * CURRENT Returns the current char value, with the full decoding of
877 * UTF-8 if we are using this mode. It returns an int.
878 * NEXT Skip to the next character, this does the proper decoding
879 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
880 * It returns the pointer to the current xmlChar.
881 */
882
883#define CUR (*ctxt->cur)
884#define SKIP(val) ctxt->cur += (val)
885#define NXT(val) ctxt->cur[(val)]
886#define CUR_PTR ctxt->cur
887
888#define SKIP_BLANKS \
889 while (IS_BLANK(*(ctxt->cur))) NEXT
890
891#define CURRENT (*ctxt->cur)
892#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
893
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000894
895#ifndef DBL_DIG
896#define DBL_DIG 16
897#endif
898#ifndef DBL_EPSILON
899#define DBL_EPSILON 1E-9
900#endif
901
902#define UPPER_DOUBLE 1E9
903#define LOWER_DOUBLE 1E-5
904
905#define INTEGER_DIGITS DBL_DIG
906#define FRACTION_DIGITS (DBL_DIG + 1)
907#define EXPONENT_DIGITS (3 + 2)
908
909/**
910 * xmlXPathFormatNumber:
911 * @number: number to format
912 * @buffer: output buffer
913 * @buffersize: size of output buffer
914 *
915 * Convert the number into a string representation.
916 */
917static void
918xmlXPathFormatNumber(double number, char buffer[], int buffersize)
919{
920 switch (isinf(number)) {
921 case 1:
922 if (buffersize > (int)sizeof("+Infinity"))
923 sprintf(buffer, "+Infinity");
924 break;
925 case -1:
926 if (buffersize > (int)sizeof("-Infinity"))
927 sprintf(buffer, "-Infinity");
928 break;
929 default:
930 if (isnan(number)) {
931 if (buffersize > (int)sizeof("NaN"))
932 sprintf(buffer, "NaN");
933 } else {
934 char work[INTEGER_DIGITS + FRACTION_DIGITS + EXPONENT_DIGITS + 1];
935 char *pointer;
936 char *start;
937 int i;
938 int digits;
939 int is_negative;
940 int use_scientific;
941 int exponent;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000942 int indx;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000943 int count;
944 double n;
945
946 i = digits = 0;
947 is_negative = (number < 0.0);
948 if (is_negative)
949 number = -number;
950
951 /* Scale number */
952 n = log10(number);
953 exponent = (isinf(n) == -1) ? 0 : (int)n;
954 use_scientific = (((number <= LOWER_DOUBLE) ||
955 (number > UPPER_DOUBLE)) &&
956 (number != 0));
957 if (use_scientific) {
958 number /= pow(10.0, (double)exponent);
959 while (number < 1.0) {
960 number *= 10.0;
961 exponent--;
962 }
963 }
964
965 /* Integer part is build from back */
966 pointer = &work[INTEGER_DIGITS + 1];
967 if (number < 1.0) {
968 *(--pointer) = '0';
969 digits++;
970 } else {
971 n = number;
972 for (i = 1; i < INTEGER_DIGITS - 1; i++) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000973 indx = (int)n % 10;
974 *(--pointer) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000975 n /= 10.0;
976 if (n < 1.0)
977 break;
978 }
979 digits += i;
980 }
981 if (is_negative) {
982 *(--pointer) = '-';
983 digits++;
984 }
985 start = pointer;
986
987 /* Fraction part is build from front */
988 i = 0;
989 pointer = &work[INTEGER_DIGITS + 1];
990 if (number - floor(number) > DBL_EPSILON) {
991 *(pointer++) = '.';
992 i++;
993 n = number;
994 count = 0;
995 while (i < FRACTION_DIGITS) {
996 n -= floor(n);
997 n *= 10.0;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000998 indx = (int)n % 10;
999 *(pointer++) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001000 i++;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001001 if ((indx != 0) || (count > 0))
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001002 count++;
1003 if ((n > 10.0) || (count > FRACTION_DIGITS / 2))
1004 break;
1005 }
1006 }
1007 /* Remove trailing zeroes */
1008 while ((pointer[-1] == '0') && (i > 0)) {
1009 pointer--;
1010 i--;
1011 }
1012 digits += i;
1013
1014 if (use_scientific) {
1015 *(pointer++) = 'e';
1016 digits++;
1017 if (exponent < 0) {
1018 *(pointer++) = '-';
1019 exponent = -exponent;
1020 } else {
1021 *(pointer++) = '+';
1022 }
1023 digits++;
1024 if (exponent >= 100)
1025 pointer += 2;
1026 else if (exponent >= 10)
1027 pointer += 1;
1028 while (exponent >= 1) {
1029 *(pointer--) = "0123456789"[exponent % 10];
1030 exponent /= 10;
1031 digits++;
1032 }
1033 }
1034
1035 if (digits >= buffersize)
1036 digits = buffersize - 1;
1037
1038 memcpy(buffer, start, digits);
1039 buffer[digits] = 0;
1040 }
1041 break;
1042 }
1043}
1044
Owen Taylor3473f882001-02-23 17:55:21 +00001045/************************************************************************
1046 * *
1047 * Error handling routines *
1048 * *
1049 ************************************************************************/
1050
1051
1052const char *xmlXPathErrorMessages[] = {
1053 "Ok",
1054 "Number encoding",
1055 "Unfinished litteral",
1056 "Start of litteral",
1057 "Expected $ for variable reference",
1058 "Undefined variable",
1059 "Invalid predicate",
1060 "Invalid expression",
1061 "Missing closing curly brace",
1062 "Unregistered function",
1063 "Invalid operand",
1064 "Invalid type",
1065 "Invalid number of arguments",
1066 "Invalid context size",
1067 "Invalid context position",
1068 "Memory allocation error",
1069 "Syntax error",
1070 "Resource error",
1071 "Sub resource error",
1072 "Undefined namespace prefix"
1073};
1074
1075/**
1076 * xmlXPathError:
1077 * @ctxt: the XPath Parser context
1078 * @file: the file name
1079 * @line: the line number
1080 * @no: the error number
1081 *
1082 * Create a new xmlNodeSetPtr of type double and of value @val
1083 *
1084 * Returns the newly created object.
1085 */
1086void
1087xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1088 int line, int no) {
1089 int n;
1090 const xmlChar *cur;
1091 const xmlChar *base;
1092
1093 xmlGenericError(xmlGenericErrorContext,
1094 "Error %s:%d: %s\n", file, line,
1095 xmlXPathErrorMessages[no]);
1096
1097 cur = ctxt->cur;
1098 base = ctxt->base;
1099 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1100 cur--;
1101 }
1102 n = 0;
1103 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1104 cur--;
1105 if ((*cur == '\n') || (*cur == '\r')) cur++;
1106 base = cur;
1107 n = 0;
1108 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1109 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1110 n++;
1111 }
1112 xmlGenericError(xmlGenericErrorContext, "\n");
1113 cur = ctxt->cur;
1114 while ((*cur == '\n') || (*cur == '\r'))
1115 cur--;
1116 n = 0;
1117 while ((cur != base) && (n++ < 80)) {
1118 xmlGenericError(xmlGenericErrorContext, " ");
1119 base++;
1120 }
1121 xmlGenericError(xmlGenericErrorContext,"^\n");
1122}
1123
1124
1125/************************************************************************
1126 * *
1127 * Routines to handle NodeSets *
1128 * *
1129 ************************************************************************/
1130
1131/**
1132 * xmlXPathCmpNodes:
1133 * @node1: the first node
1134 * @node2: the second node
1135 *
1136 * Compare two nodes w.r.t document order
1137 *
1138 * Returns -2 in case of error 1 if first point < second point, 0 if
1139 * that's the same node, -1 otherwise
1140 */
1141int
1142xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1143 int depth1, depth2;
1144 xmlNodePtr cur, root;
1145
1146 if ((node1 == NULL) || (node2 == NULL))
1147 return(-2);
1148 /*
1149 * a couple of optimizations which will avoid computations in most cases
1150 */
1151 if (node1 == node2)
1152 return(0);
1153 if (node1 == node2->prev)
1154 return(1);
1155 if (node1 == node2->next)
1156 return(-1);
1157
1158 /*
1159 * compute depth to root
1160 */
1161 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1162 if (cur == node1)
1163 return(1);
1164 depth2++;
1165 }
1166 root = cur;
1167 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1168 if (cur == node2)
1169 return(-1);
1170 depth1++;
1171 }
1172 /*
1173 * Distinct document (or distinct entities :-( ) case.
1174 */
1175 if (root != cur) {
1176 return(-2);
1177 }
1178 /*
1179 * get the nearest common ancestor.
1180 */
1181 while (depth1 > depth2) {
1182 depth1--;
1183 node1 = node1->parent;
1184 }
1185 while (depth2 > depth1) {
1186 depth2--;
1187 node2 = node2->parent;
1188 }
1189 while (node1->parent != node2->parent) {
1190 node1 = node1->parent;
1191 node2 = node2->parent;
1192 /* should not happen but just in case ... */
1193 if ((node1 == NULL) || (node2 == NULL))
1194 return(-2);
1195 }
1196 /*
1197 * Find who's first.
1198 */
1199 if (node1 == node2->next)
1200 return(-1);
1201 for (cur = node1->next;cur != NULL;cur = cur->next)
1202 if (cur == node2)
1203 return(1);
1204 return(-1); /* assume there is no sibling list corruption */
1205}
1206
1207/**
1208 * xmlXPathNodeSetSort:
1209 * @set: the node set
1210 *
1211 * Sort the node set in document order
1212 */
1213void
1214xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001215 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001216 xmlNodePtr tmp;
1217
1218 if (set == NULL)
1219 return;
1220
1221 /* Use Shell's sort to sort the node-set */
1222 len = set->nodeNr;
1223 for (incr = len / 2; incr > 0; incr /= 2) {
1224 for (i = incr; i < len; i++) {
1225 j = i - incr;
1226 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001227 if (xmlXPathCmpNodes(set->nodeTab[j],
1228 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001229 tmp = set->nodeTab[j];
1230 set->nodeTab[j] = set->nodeTab[j + incr];
1231 set->nodeTab[j + incr] = tmp;
1232 j -= incr;
1233 } else
1234 break;
1235 }
1236 }
1237 }
1238}
1239
1240#define XML_NODESET_DEFAULT 10
1241/**
1242 * xmlXPathNodeSetCreate:
1243 * @val: an initial xmlNodePtr, or NULL
1244 *
1245 * Create a new xmlNodeSetPtr of type double and of value @val
1246 *
1247 * Returns the newly created object.
1248 */
1249xmlNodeSetPtr
1250xmlXPathNodeSetCreate(xmlNodePtr val) {
1251 xmlNodeSetPtr ret;
1252
1253 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1254 if (ret == NULL) {
1255 xmlGenericError(xmlGenericErrorContext,
1256 "xmlXPathNewNodeSet: out of memory\n");
1257 return(NULL);
1258 }
1259 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1260 if (val != NULL) {
1261 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1262 sizeof(xmlNodePtr));
1263 if (ret->nodeTab == NULL) {
1264 xmlGenericError(xmlGenericErrorContext,
1265 "xmlXPathNewNodeSet: out of memory\n");
1266 return(NULL);
1267 }
1268 memset(ret->nodeTab, 0 ,
1269 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1270 ret->nodeMax = XML_NODESET_DEFAULT;
1271 ret->nodeTab[ret->nodeNr++] = val;
1272 }
1273 return(ret);
1274}
1275
1276/**
1277 * xmlXPathNodeSetAdd:
1278 * @cur: the initial node set
1279 * @val: a new xmlNodePtr
1280 *
1281 * add a new xmlNodePtr ot an existing NodeSet
1282 */
1283void
1284xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1285 int i;
1286
1287 if (val == NULL) return;
1288
1289 /*
1290 * check against doublons
1291 */
1292 for (i = 0;i < cur->nodeNr;i++)
1293 if (cur->nodeTab[i] == val) return;
1294
1295 /*
1296 * grow the nodeTab if needed
1297 */
1298 if (cur->nodeMax == 0) {
1299 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1300 sizeof(xmlNodePtr));
1301 if (cur->nodeTab == NULL) {
1302 xmlGenericError(xmlGenericErrorContext,
1303 "xmlXPathNodeSetAdd: out of memory\n");
1304 return;
1305 }
1306 memset(cur->nodeTab, 0 ,
1307 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1308 cur->nodeMax = XML_NODESET_DEFAULT;
1309 } else if (cur->nodeNr == cur->nodeMax) {
1310 xmlNodePtr *temp;
1311
1312 cur->nodeMax *= 2;
1313 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1314 sizeof(xmlNodePtr));
1315 if (temp == NULL) {
1316 xmlGenericError(xmlGenericErrorContext,
1317 "xmlXPathNodeSetAdd: out of memory\n");
1318 return;
1319 }
1320 cur->nodeTab = temp;
1321 }
1322 cur->nodeTab[cur->nodeNr++] = val;
1323}
1324
1325/**
1326 * xmlXPathNodeSetAddUnique:
1327 * @cur: the initial node set
1328 * @val: a new xmlNodePtr
1329 *
1330 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1331 * when we are sure the node is not already in the set.
1332 */
1333void
1334xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1335 if (val == NULL) return;
1336
1337 /*
1338 * grow the nodeTab if needed
1339 */
1340 if (cur->nodeMax == 0) {
1341 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1342 sizeof(xmlNodePtr));
1343 if (cur->nodeTab == NULL) {
1344 xmlGenericError(xmlGenericErrorContext,
1345 "xmlXPathNodeSetAddUnique: out of memory\n");
1346 return;
1347 }
1348 memset(cur->nodeTab, 0 ,
1349 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1350 cur->nodeMax = XML_NODESET_DEFAULT;
1351 } else if (cur->nodeNr == cur->nodeMax) {
1352 xmlNodePtr *temp;
1353
1354 cur->nodeMax *= 2;
1355 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1356 sizeof(xmlNodePtr));
1357 if (temp == NULL) {
1358 xmlGenericError(xmlGenericErrorContext,
1359 "xmlXPathNodeSetAddUnique: out of memory\n");
1360 return;
1361 }
1362 cur->nodeTab = temp;
1363 }
1364 cur->nodeTab[cur->nodeNr++] = val;
1365}
1366
1367/**
1368 * xmlXPathNodeSetMerge:
1369 * @val1: the first NodeSet or NULL
1370 * @val2: the second NodeSet
1371 *
1372 * Merges two nodesets, all nodes from @val2 are added to @val1
1373 * if @val1 is NULL, a new set is created and copied from @val2
1374 *
1375 * Returns val1 once extended or NULL in case of error.
1376 */
1377xmlNodeSetPtr
1378xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
1379 int i, j, initNr;
1380
1381 if (val2 == NULL) return(val1);
1382 if (val1 == NULL) {
1383 val1 = xmlXPathNodeSetCreate(NULL);
1384 }
1385
1386 initNr = val1->nodeNr;
1387
1388 for (i = 0;i < val2->nodeNr;i++) {
1389 /*
1390 * check against doublons
1391 */
1392 for (j = 0; j < initNr; j++)
1393 if (val1->nodeTab[j] == val2->nodeTab[i]) continue;
1394
1395 /*
1396 * grow the nodeTab if needed
1397 */
1398 if (val1->nodeMax == 0) {
1399 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1400 sizeof(xmlNodePtr));
1401 if (val1->nodeTab == NULL) {
1402 xmlGenericError(xmlGenericErrorContext,
1403 "xmlXPathNodeSetMerge: out of memory\n");
1404 return(NULL);
1405 }
1406 memset(val1->nodeTab, 0 ,
1407 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1408 val1->nodeMax = XML_NODESET_DEFAULT;
1409 } else if (val1->nodeNr == val1->nodeMax) {
1410 xmlNodePtr *temp;
1411
1412 val1->nodeMax *= 2;
1413 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1414 sizeof(xmlNodePtr));
1415 if (temp == NULL) {
1416 xmlGenericError(xmlGenericErrorContext,
1417 "xmlXPathNodeSetMerge: out of memory\n");
1418 return(NULL);
1419 }
1420 val1->nodeTab = temp;
1421 }
1422 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1423 }
1424
1425 return(val1);
1426}
1427
1428/**
1429 * xmlXPathNodeSetDel:
1430 * @cur: the initial node set
1431 * @val: an xmlNodePtr
1432 *
1433 * Removes an xmlNodePtr from an existing NodeSet
1434 */
1435void
1436xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1437 int i;
1438
1439 if (cur == NULL) return;
1440 if (val == NULL) return;
1441
1442 /*
1443 * check against doublons
1444 */
1445 for (i = 0;i < cur->nodeNr;i++)
1446 if (cur->nodeTab[i] == val) break;
1447
1448 if (i >= cur->nodeNr) {
1449#ifdef DEBUG
1450 xmlGenericError(xmlGenericErrorContext,
1451 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1452 val->name);
1453#endif
1454 return;
1455 }
1456 cur->nodeNr--;
1457 for (;i < cur->nodeNr;i++)
1458 cur->nodeTab[i] = cur->nodeTab[i + 1];
1459 cur->nodeTab[cur->nodeNr] = NULL;
1460}
1461
1462/**
1463 * xmlXPathNodeSetRemove:
1464 * @cur: the initial node set
1465 * @val: the index to remove
1466 *
1467 * Removes an entry from an existing NodeSet list.
1468 */
1469void
1470xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1471 if (cur == NULL) return;
1472 if (val >= cur->nodeNr) return;
1473 cur->nodeNr--;
1474 for (;val < cur->nodeNr;val++)
1475 cur->nodeTab[val] = cur->nodeTab[val + 1];
1476 cur->nodeTab[cur->nodeNr] = NULL;
1477}
1478
1479/**
1480 * xmlXPathFreeNodeSet:
1481 * @obj: the xmlNodeSetPtr to free
1482 *
1483 * Free the NodeSet compound (not the actual nodes !).
1484 */
1485void
1486xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1487 if (obj == NULL) return;
1488 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001489 xmlFree(obj->nodeTab);
1490 }
Owen Taylor3473f882001-02-23 17:55:21 +00001491 xmlFree(obj);
1492}
1493
1494/**
1495 * xmlXPathFreeValueTree:
1496 * @obj: the xmlNodeSetPtr to free
1497 *
1498 * Free the NodeSet compound and the actual tree, this is different
1499 * from xmlXPathFreeNodeSet()
1500 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001501static void
Owen Taylor3473f882001-02-23 17:55:21 +00001502xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1503 int i;
1504
1505 if (obj == NULL) return;
1506 for (i = 0;i < obj->nodeNr;i++)
1507 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001508 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001509
1510 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001511 xmlFree(obj->nodeTab);
1512 }
Owen Taylor3473f882001-02-23 17:55:21 +00001513 xmlFree(obj);
1514}
1515
1516#if defined(DEBUG) || defined(DEBUG_STEP)
1517/**
1518 * xmlGenericErrorContextNodeSet:
1519 * @output: a FILE * for the output
1520 * @obj: the xmlNodeSetPtr to free
1521 *
1522 * Quick display of a NodeSet
1523 */
1524void
1525xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1526 int i;
1527
1528 if (output == NULL) output = xmlGenericErrorContext;
1529 if (obj == NULL) {
1530 fprintf(output, "NodeSet == NULL !\n");
1531 return;
1532 }
1533 if (obj->nodeNr == 0) {
1534 fprintf(output, "NodeSet is empty\n");
1535 return;
1536 }
1537 if (obj->nodeTab == NULL) {
1538 fprintf(output, " nodeTab == NULL !\n");
1539 return;
1540 }
1541 for (i = 0; i < obj->nodeNr; i++) {
1542 if (obj->nodeTab[i] == NULL) {
1543 fprintf(output, " NULL !\n");
1544 return;
1545 }
1546 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1547 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1548 fprintf(output, " /");
1549 else if (obj->nodeTab[i]->name == NULL)
1550 fprintf(output, " noname!");
1551 else fprintf(output, " %s", obj->nodeTab[i]->name);
1552 }
1553 fprintf(output, "\n");
1554}
1555#endif
1556
1557/**
1558 * xmlXPathNewNodeSet:
1559 * @val: the NodePtr value
1560 *
1561 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1562 * it with the single Node @val
1563 *
1564 * Returns the newly created object.
1565 */
1566xmlXPathObjectPtr
1567xmlXPathNewNodeSet(xmlNodePtr val) {
1568 xmlXPathObjectPtr ret;
1569
1570 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1571 if (ret == NULL) {
1572 xmlGenericError(xmlGenericErrorContext,
1573 "xmlXPathNewNodeSet: out of memory\n");
1574 return(NULL);
1575 }
1576 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1577 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001578 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001579 ret->nodesetval = xmlXPathNodeSetCreate(val);
1580 return(ret);
1581}
1582
1583/**
1584 * xmlXPathNewValueTree:
1585 * @val: the NodePtr value
1586 *
1587 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1588 * it with the tree root @val
1589 *
1590 * Returns the newly created object.
1591 */
1592xmlXPathObjectPtr
1593xmlXPathNewValueTree(xmlNodePtr val) {
1594 xmlXPathObjectPtr ret;
1595
1596 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1597 if (ret == NULL) {
1598 xmlGenericError(xmlGenericErrorContext,
1599 "xmlXPathNewNodeSet: out of memory\n");
1600 return(NULL);
1601 }
1602 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1603 ret->type = XPATH_XSLT_TREE;
1604 ret->nodesetval = xmlXPathNodeSetCreate(val);
1605 return(ret);
1606}
1607
1608/**
1609 * xmlXPathNewNodeSetList:
1610 * @val: an existing NodeSet
1611 *
1612 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1613 * it with the Nodeset @val
1614 *
1615 * Returns the newly created object.
1616 */
1617xmlXPathObjectPtr
1618xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1619 xmlXPathObjectPtr ret;
1620 int i;
1621
1622 if (val == NULL)
1623 ret = NULL;
1624 else if (val->nodeTab == NULL)
1625 ret = xmlXPathNewNodeSet(NULL);
1626 else
1627 {
1628 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1629 for (i = 1; i < val->nodeNr; ++i)
1630 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1631 }
1632
1633 return(ret);
1634}
1635
1636/**
1637 * xmlXPathWrapNodeSet:
1638 * @val: the NodePtr value
1639 *
1640 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1641 *
1642 * Returns the newly created object.
1643 */
1644xmlXPathObjectPtr
1645xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1646 xmlXPathObjectPtr ret;
1647
1648 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1649 if (ret == NULL) {
1650 xmlGenericError(xmlGenericErrorContext,
1651 "xmlXPathWrapNodeSet: out of memory\n");
1652 return(NULL);
1653 }
1654 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1655 ret->type = XPATH_NODESET;
1656 ret->nodesetval = val;
1657 return(ret);
1658}
1659
1660/**
1661 * xmlXPathFreeNodeSetList:
1662 * @obj: an existing NodeSetList object
1663 *
1664 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1665 * the list contrary to xmlXPathFreeObject().
1666 */
1667void
1668xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1669 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001670 xmlFree(obj);
1671}
1672
1673/************************************************************************
1674 * *
1675 * Routines to handle extra functions *
1676 * *
1677 ************************************************************************/
1678
1679/**
1680 * xmlXPathRegisterFunc:
1681 * @ctxt: the XPath context
1682 * @name: the function name
1683 * @f: the function implementation or NULL
1684 *
1685 * Register a new function. If @f is NULL it unregisters the function
1686 *
1687 * Returns 0 in case of success, -1 in case of error
1688 */
1689int
1690xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1691 xmlXPathFunction f) {
1692 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1693}
1694
1695/**
1696 * xmlXPathRegisterFuncNS:
1697 * @ctxt: the XPath context
1698 * @name: the function name
1699 * @ns_uri: the function namespace URI
1700 * @f: the function implementation or NULL
1701 *
1702 * Register a new function. If @f is NULL it unregisters the function
1703 *
1704 * Returns 0 in case of success, -1 in case of error
1705 */
1706int
1707xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1708 const xmlChar *ns_uri, xmlXPathFunction f) {
1709 if (ctxt == NULL)
1710 return(-1);
1711 if (name == NULL)
1712 return(-1);
1713
1714 if (ctxt->funcHash == NULL)
1715 ctxt->funcHash = xmlHashCreate(0);
1716 if (ctxt->funcHash == NULL)
1717 return(-1);
1718 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1719}
1720
1721/**
1722 * xmlXPathFunctionLookup:
1723 * @ctxt: the XPath context
1724 * @name: the function name
1725 *
1726 * Search in the Function array of the context for the given
1727 * function.
1728 *
1729 * Returns the xmlXPathFunction or NULL if not found
1730 */
1731xmlXPathFunction
1732xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1733 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1734}
1735
1736/**
1737 * xmlXPathFunctionLookupNS:
1738 * @ctxt: the XPath context
1739 * @name: the function name
1740 * @ns_uri: the function namespace URI
1741 *
1742 * Search in the Function array of the context for the given
1743 * function.
1744 *
1745 * Returns the xmlXPathFunction or NULL if not found
1746 */
1747xmlXPathFunction
1748xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1749 const xmlChar *ns_uri) {
1750 if (ctxt == NULL)
1751 return(NULL);
1752 if (ctxt->funcHash == NULL)
1753 return(NULL);
1754 if (name == NULL)
1755 return(NULL);
1756
1757 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1758}
1759
1760/**
1761 * xmlXPathRegisteredFuncsCleanup:
1762 * @ctxt: the XPath context
1763 *
1764 * Cleanup the XPath context data associated to registered functions
1765 */
1766void
1767xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1768 if (ctxt == NULL)
1769 return;
1770
1771 xmlHashFree(ctxt->funcHash, NULL);
1772 ctxt->funcHash = NULL;
1773}
1774
1775/************************************************************************
1776 * *
1777 * Routines to handle Variable *
1778 * *
1779 ************************************************************************/
1780
1781/**
1782 * xmlXPathRegisterVariable:
1783 * @ctxt: the XPath context
1784 * @name: the variable name
1785 * @value: the variable value or NULL
1786 *
1787 * Register a new variable value. If @value is NULL it unregisters
1788 * the variable
1789 *
1790 * Returns 0 in case of success, -1 in case of error
1791 */
1792int
1793xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1794 xmlXPathObjectPtr value) {
1795 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1796}
1797
1798/**
1799 * xmlXPathRegisterVariableNS:
1800 * @ctxt: the XPath context
1801 * @name: the variable name
1802 * @ns_uri: the variable namespace URI
1803 * @value: the variable value or NULL
1804 *
1805 * Register a new variable value. If @value is NULL it unregisters
1806 * the variable
1807 *
1808 * Returns 0 in case of success, -1 in case of error
1809 */
1810int
1811xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1812 const xmlChar *ns_uri,
1813 xmlXPathObjectPtr value) {
1814 if (ctxt == NULL)
1815 return(-1);
1816 if (name == NULL)
1817 return(-1);
1818
1819 if (ctxt->varHash == NULL)
1820 ctxt->varHash = xmlHashCreate(0);
1821 if (ctxt->varHash == NULL)
1822 return(-1);
1823 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1824 (void *) value,
1825 (xmlHashDeallocator)xmlXPathFreeObject));
1826}
1827
1828/**
1829 * xmlXPathRegisterVariableLookup:
1830 * @ctxt: the XPath context
1831 * @f: the lookup function
1832 * @data: the lookup data
1833 *
1834 * register an external mechanism to do variable lookup
1835 */
1836void
1837xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1838 xmlXPathVariableLookupFunc f, void *data) {
1839 if (ctxt == NULL)
1840 return;
1841 ctxt->varLookupFunc = (void *) f;
1842 ctxt->varLookupData = data;
1843}
1844
1845/**
1846 * xmlXPathVariableLookup:
1847 * @ctxt: the XPath context
1848 * @name: the variable name
1849 *
1850 * Search in the Variable array of the context for the given
1851 * variable value.
1852 *
1853 * Returns the value or NULL if not found
1854 */
1855xmlXPathObjectPtr
1856xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1857 if (ctxt == NULL)
1858 return(NULL);
1859
1860 if (ctxt->varLookupFunc != NULL) {
1861 xmlXPathObjectPtr ret;
1862
1863 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1864 (ctxt->varLookupData, name, NULL);
1865 if (ret != NULL) return(ret);
1866 }
1867 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1868}
1869
1870/**
1871 * xmlXPathVariableLookupNS:
1872 * @ctxt: the XPath context
1873 * @name: the variable name
1874 * @ns_uri: the variable namespace URI
1875 *
1876 * Search in the Variable array of the context for the given
1877 * variable value.
1878 *
1879 * Returns the value or NULL if not found
1880 */
1881xmlXPathObjectPtr
1882xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1883 const xmlChar *ns_uri) {
1884 if (ctxt == NULL)
1885 return(NULL);
1886
1887 if (ctxt->varLookupFunc != NULL) {
1888 xmlXPathObjectPtr ret;
1889
1890 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1891 (ctxt->varLookupData, name, ns_uri);
1892 if (ret != NULL) return(ret);
1893 }
1894
1895 if (ctxt->varHash == NULL)
1896 return(NULL);
1897 if (name == NULL)
1898 return(NULL);
1899
1900 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1901}
1902
1903/**
1904 * xmlXPathRegisteredVariablesCleanup:
1905 * @ctxt: the XPath context
1906 *
1907 * Cleanup the XPath context data associated to registered variables
1908 */
1909void
1910xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1911 if (ctxt == NULL)
1912 return;
1913
1914 xmlHashFree(ctxt->varHash, NULL);
1915 ctxt->varHash = NULL;
1916}
1917
1918/**
1919 * xmlXPathRegisterNs:
1920 * @ctxt: the XPath context
1921 * @prefix: the namespace prefix
1922 * @ns_uri: the namespace name
1923 *
1924 * Register a new namespace. If @ns_uri is NULL it unregisters
1925 * the namespace
1926 *
1927 * Returns 0 in case of success, -1 in case of error
1928 */
1929int
1930xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1931 const xmlChar *ns_uri) {
1932 if (ctxt == NULL)
1933 return(-1);
1934 if (prefix == NULL)
1935 return(-1);
1936
1937 if (ctxt->nsHash == NULL)
1938 ctxt->nsHash = xmlHashCreate(10);
1939 if (ctxt->nsHash == NULL)
1940 return(-1);
1941 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1942 (xmlHashDeallocator)xmlFree));
1943}
1944
1945/**
1946 * xmlXPathNsLookup:
1947 * @ctxt: the XPath context
1948 * @prefix: the namespace prefix value
1949 *
1950 * Search in the namespace declaration array of the context for the given
1951 * namespace name associated to the given prefix
1952 *
1953 * Returns the value or NULL if not found
1954 */
1955const xmlChar *
1956xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1957 if (ctxt == NULL)
1958 return(NULL);
1959 if (prefix == NULL)
1960 return(NULL);
1961
1962#ifdef XML_XML_NAMESPACE
1963 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1964 return(XML_XML_NAMESPACE);
1965#endif
1966
1967 if (ctxt->nsHash == NULL)
1968 return(NULL);
1969
1970 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1971}
1972
1973/**
1974 * xmlXPathRegisteredVariablesCleanup:
1975 * @ctxt: the XPath context
1976 *
1977 * Cleanup the XPath context data associated to registered variables
1978 */
1979void
1980xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1981 if (ctxt == NULL)
1982 return;
1983
1984 xmlHashFree(ctxt->nsHash, NULL);
1985 ctxt->nsHash = NULL;
1986}
1987
1988/************************************************************************
1989 * *
1990 * Routines to handle Values *
1991 * *
1992 ************************************************************************/
1993
1994/* Allocations are terrible, one need to optimize all this !!! */
1995
1996/**
1997 * xmlXPathNewFloat:
1998 * @val: the double value
1999 *
2000 * Create a new xmlXPathObjectPtr of type double and of value @val
2001 *
2002 * Returns the newly created object.
2003 */
2004xmlXPathObjectPtr
2005xmlXPathNewFloat(double val) {
2006 xmlXPathObjectPtr ret;
2007
2008 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2009 if (ret == NULL) {
2010 xmlGenericError(xmlGenericErrorContext,
2011 "xmlXPathNewFloat: out of memory\n");
2012 return(NULL);
2013 }
2014 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2015 ret->type = XPATH_NUMBER;
2016 ret->floatval = val;
2017 return(ret);
2018}
2019
2020/**
2021 * xmlXPathNewBoolean:
2022 * @val: the boolean value
2023 *
2024 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2025 *
2026 * Returns the newly created object.
2027 */
2028xmlXPathObjectPtr
2029xmlXPathNewBoolean(int val) {
2030 xmlXPathObjectPtr ret;
2031
2032 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2033 if (ret == NULL) {
2034 xmlGenericError(xmlGenericErrorContext,
2035 "xmlXPathNewBoolean: out of memory\n");
2036 return(NULL);
2037 }
2038 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2039 ret->type = XPATH_BOOLEAN;
2040 ret->boolval = (val != 0);
2041 return(ret);
2042}
2043
2044/**
2045 * xmlXPathNewString:
2046 * @val: the xmlChar * value
2047 *
2048 * Create a new xmlXPathObjectPtr of type string and of value @val
2049 *
2050 * Returns the newly created object.
2051 */
2052xmlXPathObjectPtr
2053xmlXPathNewString(const xmlChar *val) {
2054 xmlXPathObjectPtr ret;
2055
2056 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2057 if (ret == NULL) {
2058 xmlGenericError(xmlGenericErrorContext,
2059 "xmlXPathNewString: out of memory\n");
2060 return(NULL);
2061 }
2062 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2063 ret->type = XPATH_STRING;
2064 if (val != NULL)
2065 ret->stringval = xmlStrdup(val);
2066 else
2067 ret->stringval = xmlStrdup((const xmlChar *)"");
2068 return(ret);
2069}
2070
2071/**
2072 * xmlXPathNewCString:
2073 * @val: the char * value
2074 *
2075 * Create a new xmlXPathObjectPtr of type string and of value @val
2076 *
2077 * Returns the newly created object.
2078 */
2079xmlXPathObjectPtr
2080xmlXPathNewCString(const char *val) {
2081 xmlXPathObjectPtr ret;
2082
2083 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2084 if (ret == NULL) {
2085 xmlGenericError(xmlGenericErrorContext,
2086 "xmlXPathNewCString: out of memory\n");
2087 return(NULL);
2088 }
2089 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2090 ret->type = XPATH_STRING;
2091 ret->stringval = xmlStrdup(BAD_CAST val);
2092 return(ret);
2093}
2094
2095/**
2096 * xmlXPathObjectCopy:
2097 * @val: the original object
2098 *
2099 * allocate a new copy of a given object
2100 *
2101 * Returns the newly created object.
2102 */
2103xmlXPathObjectPtr
2104xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2105 xmlXPathObjectPtr ret;
2106
2107 if (val == NULL)
2108 return(NULL);
2109
2110 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2111 if (ret == NULL) {
2112 xmlGenericError(xmlGenericErrorContext,
2113 "xmlXPathObjectCopy: out of memory\n");
2114 return(NULL);
2115 }
2116 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2117 switch (val->type) {
2118 case XPATH_BOOLEAN:
2119 case XPATH_NUMBER:
2120 case XPATH_POINT:
2121 case XPATH_RANGE:
2122 break;
2123 case XPATH_STRING:
2124 ret->stringval = xmlStrdup(val->stringval);
2125 break;
2126 case XPATH_XSLT_TREE:
2127 if ((val->nodesetval != NULL) &&
2128 (val->nodesetval->nodeTab != NULL))
2129 ret->nodesetval = xmlXPathNodeSetCreate(
2130 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2131 else
2132 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2133 break;
2134 case XPATH_NODESET:
2135 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2136 break;
2137 case XPATH_LOCATIONSET:
2138#ifdef LIBXML_XPTR_ENABLED
2139 {
2140 xmlLocationSetPtr loc = val->user;
2141 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2142 break;
2143 }
2144#endif
2145 case XPATH_UNDEFINED:
2146 case XPATH_USERS:
2147 xmlGenericError(xmlGenericErrorContext,
2148 "xmlXPathObjectCopy: unsupported type %d\n",
2149 val->type);
2150 break;
2151 }
2152 return(ret);
2153}
2154
2155/**
2156 * xmlXPathFreeObject:
2157 * @obj: the object to free
2158 *
2159 * Free up an xmlXPathObjectPtr object.
2160 */
2161void
2162xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2163 if (obj == NULL) return;
2164 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002165 if (obj->boolval) {
2166 obj->type = XPATH_XSLT_TREE;
2167 if (obj->nodesetval != NULL)
2168 xmlXPathFreeValueTree(obj->nodesetval);
2169 } else {
2170 if (obj->nodesetval != NULL)
2171 xmlXPathFreeNodeSet(obj->nodesetval);
2172 }
Owen Taylor3473f882001-02-23 17:55:21 +00002173#ifdef LIBXML_XPTR_ENABLED
2174 } else if (obj->type == XPATH_LOCATIONSET) {
2175 if (obj->user != NULL)
2176 xmlXPtrFreeLocationSet(obj->user);
2177#endif
2178 } else if (obj->type == XPATH_STRING) {
2179 if (obj->stringval != NULL)
2180 xmlFree(obj->stringval);
2181 } else if (obj->type == XPATH_XSLT_TREE) {
2182 if (obj->nodesetval != NULL)
2183 xmlXPathFreeValueTree(obj->nodesetval);
2184 }
2185
Owen Taylor3473f882001-02-23 17:55:21 +00002186 xmlFree(obj);
2187}
2188
2189/************************************************************************
2190 * *
2191 * Routines to handle XPath contexts *
2192 * *
2193 ************************************************************************/
2194
2195/**
2196 * xmlXPathNewContext:
2197 * @doc: the XML document
2198 *
2199 * Create a new xmlXPathContext
2200 *
2201 * Returns the xmlXPathContext just allocated.
2202 */
2203xmlXPathContextPtr
2204xmlXPathNewContext(xmlDocPtr doc) {
2205 xmlXPathContextPtr ret;
2206
2207 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2208 if (ret == NULL) {
2209 xmlGenericError(xmlGenericErrorContext,
2210 "xmlXPathNewContext: out of memory\n");
2211 return(NULL);
2212 }
2213 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2214 ret->doc = doc;
2215 ret->node = NULL;
2216
2217 ret->varHash = NULL;
2218
2219 ret->nb_types = 0;
2220 ret->max_types = 0;
2221 ret->types = NULL;
2222
2223 ret->funcHash = xmlHashCreate(0);
2224
2225 ret->nb_axis = 0;
2226 ret->max_axis = 0;
2227 ret->axis = NULL;
2228
2229 ret->nsHash = NULL;
2230 ret->user = NULL;
2231
2232 ret->contextSize = -1;
2233 ret->proximityPosition = -1;
2234
2235 xmlXPathRegisterAllFunctions(ret);
2236
2237 return(ret);
2238}
2239
2240/**
2241 * xmlXPathFreeContext:
2242 * @ctxt: the context to free
2243 *
2244 * Free up an xmlXPathContext
2245 */
2246void
2247xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2248 xmlXPathRegisteredNsCleanup(ctxt);
2249 xmlXPathRegisteredFuncsCleanup(ctxt);
2250 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002251 xmlFree(ctxt);
2252}
2253
2254/************************************************************************
2255 * *
2256 * Routines to handle XPath parser contexts *
2257 * *
2258 ************************************************************************/
2259
2260#define CHECK_CTXT(ctxt) \
2261 if (ctxt == NULL) { \
2262 xmlGenericError(xmlGenericErrorContext, \
2263 "%s:%d Internal error: ctxt == NULL\n", \
2264 __FILE__, __LINE__); \
2265 } \
2266
2267
2268#define CHECK_CONTEXT(ctxt) \
2269 if (ctxt == NULL) { \
2270 xmlGenericError(xmlGenericErrorContext, \
2271 "%s:%d Internal error: no context\n", \
2272 __FILE__, __LINE__); \
2273 } \
2274 else if (ctxt->doc == NULL) { \
2275 xmlGenericError(xmlGenericErrorContext, \
2276 "%s:%d Internal error: no document\n", \
2277 __FILE__, __LINE__); \
2278 } \
2279 else if (ctxt->doc->children == NULL) { \
2280 xmlGenericError(xmlGenericErrorContext, \
2281 "%s:%d Internal error: document without root\n", \
2282 __FILE__, __LINE__); \
2283 } \
2284
2285
2286/**
2287 * xmlXPathNewParserContext:
2288 * @str: the XPath expression
2289 * @ctxt: the XPath context
2290 *
2291 * Create a new xmlXPathParserContext
2292 *
2293 * Returns the xmlXPathParserContext just allocated.
2294 */
2295xmlXPathParserContextPtr
2296xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2297 xmlXPathParserContextPtr ret;
2298
2299 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2300 if (ret == NULL) {
2301 xmlGenericError(xmlGenericErrorContext,
2302 "xmlXPathNewParserContext: out of memory\n");
2303 return(NULL);
2304 }
2305 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2306 ret->cur = ret->base = str;
2307 ret->context = ctxt;
2308
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002309 ret->comp = xmlXPathNewCompExpr();
2310 if (ret->comp == NULL) {
2311 xmlFree(ret->valueTab);
2312 xmlFree(ret);
2313 return(NULL);
2314 }
2315
2316 return(ret);
2317}
2318
2319/**
2320 * xmlXPathCompParserContext:
2321 * @comp: the XPath compiled expression
2322 * @ctxt: the XPath context
2323 *
2324 * Create a new xmlXPathParserContext when processing a compiled expression
2325 *
2326 * Returns the xmlXPathParserContext just allocated.
2327 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002328static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002329xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2330 xmlXPathParserContextPtr ret;
2331
2332 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2333 if (ret == NULL) {
2334 xmlGenericError(xmlGenericErrorContext,
2335 "xmlXPathNewParserContext: out of memory\n");
2336 return(NULL);
2337 }
2338 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2339
Owen Taylor3473f882001-02-23 17:55:21 +00002340 /* Allocate the value stack */
2341 ret->valueTab = (xmlXPathObjectPtr *)
2342 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002343 if (ret->valueTab == NULL) {
2344 xmlFree(ret);
2345 xmlGenericError(xmlGenericErrorContext,
2346 "xmlXPathNewParserContext: out of memory\n");
2347 return(NULL);
2348 }
Owen Taylor3473f882001-02-23 17:55:21 +00002349 ret->valueNr = 0;
2350 ret->valueMax = 10;
2351 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002352
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002353 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002354 ret->comp = comp;
2355
Owen Taylor3473f882001-02-23 17:55:21 +00002356 return(ret);
2357}
2358
2359/**
2360 * xmlXPathFreeParserContext:
2361 * @ctxt: the context to free
2362 *
2363 * Free up an xmlXPathParserContext
2364 */
2365void
2366xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2367 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002368 xmlFree(ctxt->valueTab);
2369 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002370 if (ctxt->comp)
2371 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002372 xmlFree(ctxt);
2373}
2374
2375/************************************************************************
2376 * *
2377 * The implicit core function library *
2378 * *
2379 ************************************************************************/
2380
2381/*
2382 * Auto-pop and cast to a number
2383 */
2384void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2385
2386
2387#define POP_FLOAT \
2388 arg = valuePop(ctxt); \
2389 if (arg == NULL) { \
2390 XP_ERROR(XPATH_INVALID_OPERAND); \
2391 } \
2392 if (arg->type != XPATH_NUMBER) { \
2393 valuePush(ctxt, arg); \
2394 xmlXPathNumberFunction(ctxt, 1); \
2395 arg = valuePop(ctxt); \
2396 }
2397
2398/**
2399 * xmlXPathCompareNodeSetFloat:
2400 * @ctxt: the XPath Parser context
2401 * @inf: less than (1) or greater than (0)
2402 * @strict: is the comparison strict
2403 * @arg: the node set
2404 * @f: the value
2405 *
2406 * Implement the compare operation between a nodeset and a number
2407 * @ns < @val (1, 1, ...
2408 * @ns <= @val (1, 0, ...
2409 * @ns > @val (0, 1, ...
2410 * @ns >= @val (0, 0, ...
2411 *
2412 * If one object to be compared is a node-set and the other is a number,
2413 * then the comparison will be true if and only if there is a node in the
2414 * node-set such that the result of performing the comparison on the number
2415 * to be compared and on the result of converting the string-value of that
2416 * node to a number using the number function is true.
2417 *
2418 * Returns 0 or 1 depending on the results of the test.
2419 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002420static int
Owen Taylor3473f882001-02-23 17:55:21 +00002421xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2422 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2423 int i, ret = 0;
2424 xmlNodeSetPtr ns;
2425 xmlChar *str2;
2426
2427 if ((f == NULL) || (arg == NULL) ||
2428 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2429 xmlXPathFreeObject(arg);
2430 xmlXPathFreeObject(f);
2431 return(0);
2432 }
2433 ns = arg->nodesetval;
2434 for (i = 0;i < ns->nodeNr;i++) {
2435 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2436 if (str2 != NULL) {
2437 valuePush(ctxt,
2438 xmlXPathNewString(str2));
2439 xmlFree(str2);
2440 xmlXPathNumberFunction(ctxt, 1);
2441 valuePush(ctxt, xmlXPathObjectCopy(f));
2442 ret = xmlXPathCompareValues(ctxt, inf, strict);
2443 if (ret)
2444 break;
2445 }
2446 }
2447 xmlXPathFreeObject(arg);
2448 xmlXPathFreeObject(f);
2449 return(ret);
2450}
2451
2452/**
2453 * xmlXPathCompareNodeSetString:
2454 * @ctxt: the XPath Parser context
2455 * @inf: less than (1) or greater than (0)
2456 * @strict: is the comparison strict
2457 * @arg: the node set
2458 * @s: the value
2459 *
2460 * Implement the compare operation between a nodeset and a string
2461 * @ns < @val (1, 1, ...
2462 * @ns <= @val (1, 0, ...
2463 * @ns > @val (0, 1, ...
2464 * @ns >= @val (0, 0, ...
2465 *
2466 * If one object to be compared is a node-set and the other is a string,
2467 * then the comparison will be true if and only if there is a node in
2468 * the node-set such that the result of performing the comparison on the
2469 * string-value of the node and the other string is true.
2470 *
2471 * Returns 0 or 1 depending on the results of the test.
2472 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002473static int
Owen Taylor3473f882001-02-23 17:55:21 +00002474xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2475 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2476 int i, ret = 0;
2477 xmlNodeSetPtr ns;
2478 xmlChar *str2;
2479
2480 if ((s == NULL) || (arg == NULL) ||
2481 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2482 xmlXPathFreeObject(arg);
2483 xmlXPathFreeObject(s);
2484 return(0);
2485 }
2486 ns = arg->nodesetval;
2487 for (i = 0;i < ns->nodeNr;i++) {
2488 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2489 if (str2 != NULL) {
2490 valuePush(ctxt,
2491 xmlXPathNewString(str2));
2492 xmlFree(str2);
2493 valuePush(ctxt, xmlXPathObjectCopy(s));
2494 ret = xmlXPathCompareValues(ctxt, inf, strict);
2495 if (ret)
2496 break;
2497 }
2498 }
2499 xmlXPathFreeObject(arg);
2500 xmlXPathFreeObject(s);
2501 return(ret);
2502}
2503
2504/**
2505 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002506 * @op: less than (-1), equal (0) or greater than (1)
2507 * @strict: is the comparison strict
2508 * @arg1: the fist node set object
2509 * @arg2: the second node set object
2510 *
2511 * Implement the compare operation on nodesets:
2512 *
2513 * If both objects to be compared are node-sets, then the comparison
2514 * will be true if and only if there is a node in the first node-set
2515 * and a node in the second node-set such that the result of performing
2516 * the comparison on the string-values of the two nodes is true.
2517 * ....
2518 * When neither object to be compared is a node-set and the operator
2519 * is <=, <, >= or >, then the objects are compared by converting both
2520 * objects to numbers and comparing the numbers according to IEEE 754.
2521 * ....
2522 * The number function converts its argument to a number as follows:
2523 * - a string that consists of optional whitespace followed by an
2524 * optional minus sign followed by a Number followed by whitespace
2525 * is converted to the IEEE 754 number that is nearest (according
2526 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2527 * represented by the string; any other string is converted to NaN
2528 *
2529 * Conclusion all nodes need to be converted first to their string value
2530 * and then the comparison must be done when possible
2531 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002532static int
2533xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002534 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2535 int i, j, init = 0;
2536 double val1;
2537 double *values2;
2538 int ret = 0;
2539 xmlChar *str;
2540 xmlNodeSetPtr ns1;
2541 xmlNodeSetPtr ns2;
2542
2543 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002544 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2545 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002546 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002547 }
Owen Taylor3473f882001-02-23 17:55:21 +00002548 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002549 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2550 xmlXPathFreeObject(arg1);
2551 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002552 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002553 }
Owen Taylor3473f882001-02-23 17:55:21 +00002554
2555 ns1 = arg1->nodesetval;
2556 ns2 = arg2->nodesetval;
2557
Daniel Veillard4dd93462001-04-02 15:16:19 +00002558 if (ns1->nodeNr <= 0) {
2559 xmlXPathFreeObject(arg1);
2560 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002561 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002562 }
2563 if (ns2->nodeNr <= 0) {
2564 xmlXPathFreeObject(arg1);
2565 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002566 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002567 }
Owen Taylor3473f882001-02-23 17:55:21 +00002568
2569 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2570 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002571 xmlXPathFreeObject(arg1);
2572 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002573 return(0);
2574 }
2575 for (i = 0;i < ns1->nodeNr;i++) {
2576 str = xmlNodeGetContent(ns1->nodeTab[i]);
2577 if (str == NULL)
2578 continue;
2579 val1 = xmlXPathStringEvalNumber(str);
2580 xmlFree(str);
2581 if (isnan(val1))
2582 continue;
2583 for (j = 0;j < ns2->nodeNr;j++) {
2584 if (init == 0) {
2585 str = xmlNodeGetContent(ns2->nodeTab[j]);
2586 if (str == NULL) {
2587 values2[j] = xmlXPathNAN;
2588 } else {
2589 values2[j] = xmlXPathStringEvalNumber(str);
2590 xmlFree(str);
2591 }
2592 }
2593 if (isnan(values2[j]))
2594 continue;
2595 if (inf && strict)
2596 ret = (val1 < values2[j]);
2597 else if (inf && !strict)
2598 ret = (val1 <= values2[j]);
2599 else if (!inf && strict)
2600 ret = (val1 > values2[j]);
2601 else if (!inf && !strict)
2602 ret = (val1 >= values2[j]);
2603 if (ret)
2604 break;
2605 }
2606 if (ret)
2607 break;
2608 init = 1;
2609 }
2610 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002611 xmlXPathFreeObject(arg1);
2612 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002613 return(ret);
2614 return(0);
2615}
2616
2617/**
2618 * xmlXPathCompareNodeSetValue:
2619 * @ctxt: the XPath Parser context
2620 * @inf: less than (1) or greater than (0)
2621 * @strict: is the comparison strict
2622 * @arg: the node set
2623 * @val: the value
2624 *
2625 * Implement the compare operation between a nodeset and a value
2626 * @ns < @val (1, 1, ...
2627 * @ns <= @val (1, 0, ...
2628 * @ns > @val (0, 1, ...
2629 * @ns >= @val (0, 0, ...
2630 *
2631 * If one object to be compared is a node-set and the other is a boolean,
2632 * then the comparison will be true if and only if the result of performing
2633 * the comparison on the boolean and on the result of converting
2634 * the node-set to a boolean using the boolean function is true.
2635 *
2636 * Returns 0 or 1 depending on the results of the test.
2637 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002638static int
Owen Taylor3473f882001-02-23 17:55:21 +00002639xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2640 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2641 if ((val == NULL) || (arg == NULL) ||
2642 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2643 return(0);
2644
2645 switch(val->type) {
2646 case XPATH_NUMBER:
2647 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2648 case XPATH_NODESET:
2649 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002650 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002651 case XPATH_STRING:
2652 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2653 case XPATH_BOOLEAN:
2654 valuePush(ctxt, arg);
2655 xmlXPathBooleanFunction(ctxt, 1);
2656 valuePush(ctxt, val);
2657 return(xmlXPathCompareValues(ctxt, inf, strict));
2658 default:
2659 TODO
2660 return(0);
2661 }
2662 return(0);
2663}
2664
2665/**
2666 * xmlXPathEqualNodeSetString
2667 * @arg: the nodeset object argument
2668 * @str: the string to compare to.
2669 *
2670 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2671 * If one object to be compared is a node-set and the other is a string,
2672 * then the comparison will be true if and only if there is a node in
2673 * the node-set such that the result of performing the comparison on the
2674 * string-value of the node and the other string is true.
2675 *
2676 * Returns 0 or 1 depending on the results of the test.
2677 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002678static int
Owen Taylor3473f882001-02-23 17:55:21 +00002679xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2680 int i;
2681 xmlNodeSetPtr ns;
2682 xmlChar *str2;
2683
2684 if ((str == NULL) || (arg == NULL) ||
2685 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2686 return(0);
2687 ns = arg->nodesetval;
2688 if (ns->nodeNr <= 0)
2689 return(0);
2690 for (i = 0;i < ns->nodeNr;i++) {
2691 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2692 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2693 xmlFree(str2);
2694 return(1);
2695 }
2696 if (str2 != NULL)
2697 xmlFree(str2);
2698 }
2699 return(0);
2700}
2701
2702/**
2703 * xmlXPathEqualNodeSetFloat
2704 * @arg: the nodeset object argument
2705 * @f: the float to compare to
2706 *
2707 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2708 * If one object to be compared is a node-set and the other is a number,
2709 * then the comparison will be true if and only if there is a node in
2710 * the node-set such that the result of performing the comparison on the
2711 * number to be compared and on the result of converting the string-value
2712 * of that node to a number using the number function is true.
2713 *
2714 * Returns 0 or 1 depending on the results of the test.
2715 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002716static int
Owen Taylor3473f882001-02-23 17:55:21 +00002717xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2718 char buf[100] = "";
2719
2720 if ((arg == NULL) ||
2721 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2722 return(0);
2723
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002724 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002725 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2726}
2727
2728
2729/**
2730 * xmlXPathEqualNodeSets
2731 * @arg1: first nodeset object argument
2732 * @arg2: second nodeset object argument
2733 *
2734 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2735 * If both objects to be compared are node-sets, then the comparison
2736 * will be true if and only if there is a node in the first node-set and
2737 * a node in the second node-set such that the result of performing the
2738 * comparison on the string-values of the two nodes is true.
2739 *
2740 * (needless to say, this is a costly operation)
2741 *
2742 * Returns 0 or 1 depending on the results of the test.
2743 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002744static int
Owen Taylor3473f882001-02-23 17:55:21 +00002745xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2746 int i, j;
2747 xmlChar **values1;
2748 xmlChar **values2;
2749 int ret = 0;
2750 xmlNodeSetPtr ns1;
2751 xmlNodeSetPtr ns2;
2752
2753 if ((arg1 == NULL) ||
2754 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2755 return(0);
2756 if ((arg2 == NULL) ||
2757 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2758 return(0);
2759
2760 ns1 = arg1->nodesetval;
2761 ns2 = arg2->nodesetval;
2762
2763 if (ns1->nodeNr <= 0)
2764 return(0);
2765 if (ns2->nodeNr <= 0)
2766 return(0);
2767
2768 /*
2769 * check if there is a node pertaining to both sets
2770 */
2771 for (i = 0;i < ns1->nodeNr;i++)
2772 for (j = 0;j < ns2->nodeNr;j++)
2773 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2774 return(1);
2775
2776 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2777 if (values1 == NULL)
2778 return(0);
2779 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2780 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2781 if (values2 == NULL) {
2782 xmlFree(values1);
2783 return(0);
2784 }
2785 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2786 for (i = 0;i < ns1->nodeNr;i++) {
2787 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2788 for (j = 0;j < ns2->nodeNr;j++) {
2789 if (i == 0)
2790 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2791 ret = xmlStrEqual(values1[i], values2[j]);
2792 if (ret)
2793 break;
2794 }
2795 if (ret)
2796 break;
2797 }
2798 for (i = 0;i < ns1->nodeNr;i++)
2799 if (values1[i] != NULL)
2800 xmlFree(values1[i]);
2801 for (j = 0;j < ns2->nodeNr;j++)
2802 if (values2[j] != NULL)
2803 xmlFree(values2[j]);
2804 xmlFree(values1);
2805 xmlFree(values2);
2806 return(ret);
2807}
2808
2809/**
2810 * xmlXPathEqualValues:
2811 * @ctxt: the XPath Parser context
2812 *
2813 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2814 *
2815 * Returns 0 or 1 depending on the results of the test.
2816 */
2817int
2818xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2819 xmlXPathObjectPtr arg1, arg2;
2820 int ret = 0;
2821
2822 arg1 = valuePop(ctxt);
2823 if (arg1 == NULL)
2824 XP_ERROR0(XPATH_INVALID_OPERAND);
2825
2826 arg2 = valuePop(ctxt);
2827 if (arg2 == NULL) {
2828 xmlXPathFreeObject(arg1);
2829 XP_ERROR0(XPATH_INVALID_OPERAND);
2830 }
2831
2832 if (arg1 == arg2) {
2833#ifdef DEBUG_EXPR
2834 xmlGenericError(xmlGenericErrorContext,
2835 "Equal: by pointer\n");
2836#endif
2837 return(1);
2838 }
2839
2840 switch (arg1->type) {
2841 case XPATH_UNDEFINED:
2842#ifdef DEBUG_EXPR
2843 xmlGenericError(xmlGenericErrorContext,
2844 "Equal: undefined\n");
2845#endif
2846 break;
2847 case XPATH_XSLT_TREE:
2848 case XPATH_NODESET:
2849 switch (arg2->type) {
2850 case XPATH_UNDEFINED:
2851#ifdef DEBUG_EXPR
2852 xmlGenericError(xmlGenericErrorContext,
2853 "Equal: undefined\n");
2854#endif
2855 break;
2856 case XPATH_XSLT_TREE:
2857 case XPATH_NODESET:
2858 ret = xmlXPathEqualNodeSets(arg1, arg2);
2859 break;
2860 case XPATH_BOOLEAN:
2861 if ((arg1->nodesetval == NULL) ||
2862 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2863 else
2864 ret = 1;
2865 ret = (ret == arg2->boolval);
2866 break;
2867 case XPATH_NUMBER:
2868 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2869 break;
2870 case XPATH_STRING:
2871 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2872 break;
2873 case XPATH_USERS:
2874 case XPATH_POINT:
2875 case XPATH_RANGE:
2876 case XPATH_LOCATIONSET:
2877 TODO
2878 break;
2879 }
2880 break;
2881 case XPATH_BOOLEAN:
2882 switch (arg2->type) {
2883 case XPATH_UNDEFINED:
2884#ifdef DEBUG_EXPR
2885 xmlGenericError(xmlGenericErrorContext,
2886 "Equal: undefined\n");
2887#endif
2888 break;
2889 case XPATH_NODESET:
2890 case XPATH_XSLT_TREE:
2891 if ((arg2->nodesetval == NULL) ||
2892 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2893 else
2894 ret = 1;
2895 break;
2896 case XPATH_BOOLEAN:
2897#ifdef DEBUG_EXPR
2898 xmlGenericError(xmlGenericErrorContext,
2899 "Equal: %d boolean %d \n",
2900 arg1->boolval, arg2->boolval);
2901#endif
2902 ret = (arg1->boolval == arg2->boolval);
2903 break;
2904 case XPATH_NUMBER:
2905 if (arg2->floatval) ret = 1;
2906 else ret = 0;
2907 ret = (arg1->boolval == ret);
2908 break;
2909 case XPATH_STRING:
2910 if ((arg2->stringval == NULL) ||
2911 (arg2->stringval[0] == 0)) ret = 0;
2912 else
2913 ret = 1;
2914 ret = (arg1->boolval == ret);
2915 break;
2916 case XPATH_USERS:
2917 case XPATH_POINT:
2918 case XPATH_RANGE:
2919 case XPATH_LOCATIONSET:
2920 TODO
2921 break;
2922 }
2923 break;
2924 case XPATH_NUMBER:
2925 switch (arg2->type) {
2926 case XPATH_UNDEFINED:
2927#ifdef DEBUG_EXPR
2928 xmlGenericError(xmlGenericErrorContext,
2929 "Equal: undefined\n");
2930#endif
2931 break;
2932 case XPATH_NODESET:
2933 case XPATH_XSLT_TREE:
2934 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2935 break;
2936 case XPATH_BOOLEAN:
2937 if (arg1->floatval) ret = 1;
2938 else ret = 0;
2939 ret = (arg2->boolval == ret);
2940 break;
2941 case XPATH_STRING:
2942 valuePush(ctxt, arg2);
2943 xmlXPathNumberFunction(ctxt, 1);
2944 arg2 = valuePop(ctxt);
2945 /* no break on purpose */
2946 case XPATH_NUMBER:
2947 ret = (arg1->floatval == arg2->floatval);
2948 break;
2949 case XPATH_USERS:
2950 case XPATH_POINT:
2951 case XPATH_RANGE:
2952 case XPATH_LOCATIONSET:
2953 TODO
2954 break;
2955 }
2956 break;
2957 case XPATH_STRING:
2958 switch (arg2->type) {
2959 case XPATH_UNDEFINED:
2960#ifdef DEBUG_EXPR
2961 xmlGenericError(xmlGenericErrorContext,
2962 "Equal: undefined\n");
2963#endif
2964 break;
2965 case XPATH_NODESET:
2966 case XPATH_XSLT_TREE:
2967 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2968 break;
2969 case XPATH_BOOLEAN:
2970 if ((arg1->stringval == NULL) ||
2971 (arg1->stringval[0] == 0)) ret = 0;
2972 else
2973 ret = 1;
2974 ret = (arg2->boolval == ret);
2975 break;
2976 case XPATH_STRING:
2977 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2978 break;
2979 case XPATH_NUMBER:
2980 valuePush(ctxt, arg1);
2981 xmlXPathNumberFunction(ctxt, 1);
2982 arg1 = valuePop(ctxt);
2983 ret = (arg1->floatval == arg2->floatval);
2984 break;
2985 case XPATH_USERS:
2986 case XPATH_POINT:
2987 case XPATH_RANGE:
2988 case XPATH_LOCATIONSET:
2989 TODO
2990 break;
2991 }
2992 break;
2993 case XPATH_USERS:
2994 case XPATH_POINT:
2995 case XPATH_RANGE:
2996 case XPATH_LOCATIONSET:
2997 TODO
2998 break;
2999 }
3000 xmlXPathFreeObject(arg1);
3001 xmlXPathFreeObject(arg2);
3002 return(ret);
3003}
3004
3005
3006/**
3007 * xmlXPathCompareValues:
3008 * @ctxt: the XPath Parser context
3009 * @inf: less than (1) or greater than (0)
3010 * @strict: is the comparison strict
3011 *
3012 * Implement the compare operation on XPath objects:
3013 * @arg1 < @arg2 (1, 1, ...
3014 * @arg1 <= @arg2 (1, 0, ...
3015 * @arg1 > @arg2 (0, 1, ...
3016 * @arg1 >= @arg2 (0, 0, ...
3017 *
3018 * When neither object to be compared is a node-set and the operator is
3019 * <=, <, >=, >, then the objects are compared by converted both objects
3020 * to numbers and comparing the numbers according to IEEE 754. The <
3021 * comparison will be true if and only if the first number is less than the
3022 * second number. The <= comparison will be true if and only if the first
3023 * number is less than or equal to the second number. The > comparison
3024 * will be true if and only if the first number is greater than the second
3025 * number. The >= comparison will be true if and only if the first number
3026 * is greater than or equal to the second number.
3027 *
3028 * Returns 1 if the comparaison succeeded, 0 if it failed
3029 */
3030int
3031xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3032 int ret = 0;
3033 xmlXPathObjectPtr arg1, arg2;
3034
3035 arg2 = valuePop(ctxt);
3036 if (arg2 == NULL) {
3037 XP_ERROR0(XPATH_INVALID_OPERAND);
3038 }
3039
3040 arg1 = valuePop(ctxt);
3041 if (arg1 == NULL) {
3042 xmlXPathFreeObject(arg2);
3043 XP_ERROR0(XPATH_INVALID_OPERAND);
3044 }
3045
3046 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3047 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003048 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003049 } else {
3050 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003051 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3052 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003053 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003054 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3055 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003056 }
3057 }
3058 return(ret);
3059 }
3060
3061 if (arg1->type != XPATH_NUMBER) {
3062 valuePush(ctxt, arg1);
3063 xmlXPathNumberFunction(ctxt, 1);
3064 arg1 = valuePop(ctxt);
3065 }
3066 if (arg1->type != XPATH_NUMBER) {
3067 xmlXPathFreeObject(arg1);
3068 xmlXPathFreeObject(arg2);
3069 XP_ERROR0(XPATH_INVALID_OPERAND);
3070 }
3071 if (arg2->type != XPATH_NUMBER) {
3072 valuePush(ctxt, arg2);
3073 xmlXPathNumberFunction(ctxt, 1);
3074 arg2 = valuePop(ctxt);
3075 }
3076 if (arg2->type != XPATH_NUMBER) {
3077 xmlXPathFreeObject(arg1);
3078 xmlXPathFreeObject(arg2);
3079 XP_ERROR0(XPATH_INVALID_OPERAND);
3080 }
3081 /*
3082 * Add tests for infinity and nan
3083 * => feedback on 3.4 for Inf and NaN
3084 */
3085 if (inf && strict)
3086 ret = (arg1->floatval < arg2->floatval);
3087 else if (inf && !strict)
3088 ret = (arg1->floatval <= arg2->floatval);
3089 else if (!inf && strict)
3090 ret = (arg1->floatval > arg2->floatval);
3091 else if (!inf && !strict)
3092 ret = (arg1->floatval >= arg2->floatval);
3093 xmlXPathFreeObject(arg1);
3094 xmlXPathFreeObject(arg2);
3095 return(ret);
3096}
3097
3098/**
3099 * xmlXPathValueFlipSign:
3100 * @ctxt: the XPath Parser context
3101 *
3102 * Implement the unary - operation on an XPath object
3103 * The numeric operators convert their operands to numbers as if
3104 * by calling the number function.
3105 */
3106void
3107xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3108 xmlXPathObjectPtr arg;
3109
3110 POP_FLOAT
3111 arg->floatval = -arg->floatval;
3112 valuePush(ctxt, arg);
3113}
3114
3115/**
3116 * xmlXPathAddValues:
3117 * @ctxt: the XPath Parser context
3118 *
3119 * Implement the add operation on XPath objects:
3120 * The numeric operators convert their operands to numbers as if
3121 * by calling the number function.
3122 */
3123void
3124xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3125 xmlXPathObjectPtr arg;
3126 double val;
3127
3128 POP_FLOAT
3129 val = arg->floatval;
3130 xmlXPathFreeObject(arg);
3131
3132 POP_FLOAT
3133 arg->floatval += val;
3134 valuePush(ctxt, arg);
3135}
3136
3137/**
3138 * xmlXPathSubValues:
3139 * @ctxt: the XPath Parser context
3140 *
3141 * Implement the substraction operation on XPath objects:
3142 * The numeric operators convert their operands to numbers as if
3143 * by calling the number function.
3144 */
3145void
3146xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3147 xmlXPathObjectPtr arg;
3148 double val;
3149
3150 POP_FLOAT
3151 val = arg->floatval;
3152 xmlXPathFreeObject(arg);
3153
3154 POP_FLOAT
3155 arg->floatval -= val;
3156 valuePush(ctxt, arg);
3157}
3158
3159/**
3160 * xmlXPathMultValues:
3161 * @ctxt: the XPath Parser context
3162 *
3163 * Implement the multiply operation on XPath objects:
3164 * The numeric operators convert their operands to numbers as if
3165 * by calling the number function.
3166 */
3167void
3168xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3169 xmlXPathObjectPtr arg;
3170 double val;
3171
3172 POP_FLOAT
3173 val = arg->floatval;
3174 xmlXPathFreeObject(arg);
3175
3176 POP_FLOAT
3177 arg->floatval *= val;
3178 valuePush(ctxt, arg);
3179}
3180
3181/**
3182 * xmlXPathDivValues:
3183 * @ctxt: the XPath Parser context
3184 *
3185 * Implement the div operation on XPath objects @arg1 / @arg2:
3186 * The numeric operators convert their operands to numbers as if
3187 * by calling the number function.
3188 */
3189void
3190xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3191 xmlXPathObjectPtr arg;
3192 double val;
3193
3194 POP_FLOAT
3195 val = arg->floatval;
3196 xmlXPathFreeObject(arg);
3197
3198 POP_FLOAT
3199 arg->floatval /= val;
3200 valuePush(ctxt, arg);
3201}
3202
3203/**
3204 * xmlXPathModValues:
3205 * @ctxt: the XPath Parser context
3206 *
3207 * Implement the mod operation on XPath objects: @arg1 / @arg2
3208 * The numeric operators convert their operands to numbers as if
3209 * by calling the number function.
3210 */
3211void
3212xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3213 xmlXPathObjectPtr arg;
3214 int arg1, arg2;
3215
3216 POP_FLOAT
3217 arg2 = (int) arg->floatval;
3218 xmlXPathFreeObject(arg);
3219
3220 POP_FLOAT
3221 arg1 = (int) arg->floatval;
3222 arg->floatval = arg1 % arg2;
3223 valuePush(ctxt, arg);
3224}
3225
3226/************************************************************************
3227 * *
3228 * The traversal functions *
3229 * *
3230 ************************************************************************/
3231
Owen Taylor3473f882001-02-23 17:55:21 +00003232/*
3233 * A traversal function enumerates nodes along an axis.
3234 * Initially it must be called with NULL, and it indicates
3235 * termination on the axis by returning NULL.
3236 */
3237typedef xmlNodePtr (*xmlXPathTraversalFunction)
3238 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3239
3240/**
3241 * xmlXPathNextSelf:
3242 * @ctxt: the XPath Parser context
3243 * @cur: the current node in the traversal
3244 *
3245 * Traversal function for the "self" direction
3246 * The self axis contains just the context node itself
3247 *
3248 * Returns the next element following that axis
3249 */
3250xmlNodePtr
3251xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3252 if (cur == NULL)
3253 return(ctxt->context->node);
3254 return(NULL);
3255}
3256
3257/**
3258 * xmlXPathNextChild:
3259 * @ctxt: the XPath Parser context
3260 * @cur: the current node in the traversal
3261 *
3262 * Traversal function for the "child" direction
3263 * The child axis contains the children of the context node in document order.
3264 *
3265 * Returns the next element following that axis
3266 */
3267xmlNodePtr
3268xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3269 if (cur == NULL) {
3270 if (ctxt->context->node == NULL) return(NULL);
3271 switch (ctxt->context->node->type) {
3272 case XML_ELEMENT_NODE:
3273 case XML_TEXT_NODE:
3274 case XML_CDATA_SECTION_NODE:
3275 case XML_ENTITY_REF_NODE:
3276 case XML_ENTITY_NODE:
3277 case XML_PI_NODE:
3278 case XML_COMMENT_NODE:
3279 case XML_NOTATION_NODE:
3280 case XML_DTD_NODE:
3281 return(ctxt->context->node->children);
3282 case XML_DOCUMENT_NODE:
3283 case XML_DOCUMENT_TYPE_NODE:
3284 case XML_DOCUMENT_FRAG_NODE:
3285 case XML_HTML_DOCUMENT_NODE:
3286#ifdef LIBXML_SGML_ENABLED
3287 case XML_SGML_DOCUMENT_NODE:
3288#endif
3289 return(((xmlDocPtr) ctxt->context->node)->children);
3290 case XML_ELEMENT_DECL:
3291 case XML_ATTRIBUTE_DECL:
3292 case XML_ENTITY_DECL:
3293 case XML_ATTRIBUTE_NODE:
3294 case XML_NAMESPACE_DECL:
3295 case XML_XINCLUDE_START:
3296 case XML_XINCLUDE_END:
3297 return(NULL);
3298 }
3299 return(NULL);
3300 }
3301 if ((cur->type == XML_DOCUMENT_NODE) ||
3302 (cur->type == XML_HTML_DOCUMENT_NODE))
3303 return(NULL);
3304 return(cur->next);
3305}
3306
3307/**
3308 * xmlXPathNextDescendant:
3309 * @ctxt: the XPath Parser context
3310 * @cur: the current node in the traversal
3311 *
3312 * Traversal function for the "descendant" direction
3313 * the descendant axis contains the descendants of the context node in document
3314 * order; a descendant is a child or a child of a child and so on.
3315 *
3316 * Returns the next element following that axis
3317 */
3318xmlNodePtr
3319xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3320 if (cur == NULL) {
3321 if (ctxt->context->node == NULL)
3322 return(NULL);
3323 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3324 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3325 return(NULL);
3326
3327 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3328 return(ctxt->context->doc->children);
3329 return(ctxt->context->node->children);
3330 }
3331
3332 if (cur->children != NULL)
3333 {
3334 if (cur->children->type != XML_ENTITY_DECL)
3335 return(cur->children);
3336 }
3337 if (cur->next != NULL) return(cur->next);
3338
3339 do {
3340 cur = cur->parent;
3341 if (cur == NULL) return(NULL);
3342 if (cur == ctxt->context->node) return(NULL);
3343 if (cur->next != NULL) {
3344 cur = cur->next;
3345 return(cur);
3346 }
3347 } while (cur != NULL);
3348 return(cur);
3349}
3350
3351/**
3352 * xmlXPathNextDescendantOrSelf:
3353 * @ctxt: the XPath Parser context
3354 * @cur: the current node in the traversal
3355 *
3356 * Traversal function for the "descendant-or-self" direction
3357 * the descendant-or-self axis contains the context node and the descendants
3358 * of the context node in document order; thus the context node is the first
3359 * node on the axis, and the first child of the context node is the second node
3360 * on the axis
3361 *
3362 * Returns the next element following that axis
3363 */
3364xmlNodePtr
3365xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3366 if (cur == NULL) {
3367 if (ctxt->context->node == NULL)
3368 return(NULL);
3369 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3370 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3371 return(NULL);
3372 return(ctxt->context->node);
3373 }
3374
3375 return(xmlXPathNextDescendant(ctxt, cur));
3376}
3377
3378/**
3379 * xmlXPathNextParent:
3380 * @ctxt: the XPath Parser context
3381 * @cur: the current node in the traversal
3382 *
3383 * Traversal function for the "parent" direction
3384 * The parent axis contains the parent of the context node, if there is one.
3385 *
3386 * Returns the next element following that axis
3387 */
3388xmlNodePtr
3389xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3390 /*
3391 * the parent of an attribute or namespace node is the element
3392 * to which the attribute or namespace node is attached
3393 * Namespace handling !!!
3394 */
3395 if (cur == NULL) {
3396 if (ctxt->context->node == NULL) return(NULL);
3397 switch (ctxt->context->node->type) {
3398 case XML_ELEMENT_NODE:
3399 case XML_TEXT_NODE:
3400 case XML_CDATA_SECTION_NODE:
3401 case XML_ENTITY_REF_NODE:
3402 case XML_ENTITY_NODE:
3403 case XML_PI_NODE:
3404 case XML_COMMENT_NODE:
3405 case XML_NOTATION_NODE:
3406 case XML_DTD_NODE:
3407 case XML_ELEMENT_DECL:
3408 case XML_ATTRIBUTE_DECL:
3409 case XML_XINCLUDE_START:
3410 case XML_XINCLUDE_END:
3411 case XML_ENTITY_DECL:
3412 if (ctxt->context->node->parent == NULL)
3413 return((xmlNodePtr) ctxt->context->doc);
3414 return(ctxt->context->node->parent);
3415 case XML_ATTRIBUTE_NODE: {
3416 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3417
3418 return(att->parent);
3419 }
3420 case XML_DOCUMENT_NODE:
3421 case XML_DOCUMENT_TYPE_NODE:
3422 case XML_DOCUMENT_FRAG_NODE:
3423 case XML_HTML_DOCUMENT_NODE:
3424#ifdef LIBXML_SGML_ENABLED
3425 case XML_SGML_DOCUMENT_NODE:
3426#endif
3427 return(NULL);
3428 case XML_NAMESPACE_DECL:
3429 /*
3430 * TODO !!! may require extending struct _xmlNs with
3431 * parent field
3432 * C.f. Infoset case...
3433 */
3434 return(NULL);
3435 }
3436 }
3437 return(NULL);
3438}
3439
3440/**
3441 * xmlXPathNextAncestor:
3442 * @ctxt: the XPath Parser context
3443 * @cur: the current node in the traversal
3444 *
3445 * Traversal function for the "ancestor" direction
3446 * the ancestor axis contains the ancestors of the context node; the ancestors
3447 * of the context node consist of the parent of context node and the parent's
3448 * parent and so on; the nodes are ordered in reverse document order; thus the
3449 * parent is the first node on the axis, and the parent's parent is the second
3450 * node on the axis
3451 *
3452 * Returns the next element following that axis
3453 */
3454xmlNodePtr
3455xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3456 /*
3457 * the parent of an attribute or namespace node is the element
3458 * to which the attribute or namespace node is attached
3459 * !!!!!!!!!!!!!
3460 */
3461 if (cur == NULL) {
3462 if (ctxt->context->node == NULL) return(NULL);
3463 switch (ctxt->context->node->type) {
3464 case XML_ELEMENT_NODE:
3465 case XML_TEXT_NODE:
3466 case XML_CDATA_SECTION_NODE:
3467 case XML_ENTITY_REF_NODE:
3468 case XML_ENTITY_NODE:
3469 case XML_PI_NODE:
3470 case XML_COMMENT_NODE:
3471 case XML_DTD_NODE:
3472 case XML_ELEMENT_DECL:
3473 case XML_ATTRIBUTE_DECL:
3474 case XML_ENTITY_DECL:
3475 case XML_NOTATION_NODE:
3476 case XML_XINCLUDE_START:
3477 case XML_XINCLUDE_END:
3478 if (ctxt->context->node->parent == NULL)
3479 return((xmlNodePtr) ctxt->context->doc);
3480 return(ctxt->context->node->parent);
3481 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003482 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003483
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003484 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003485 }
3486 case XML_DOCUMENT_NODE:
3487 case XML_DOCUMENT_TYPE_NODE:
3488 case XML_DOCUMENT_FRAG_NODE:
3489 case XML_HTML_DOCUMENT_NODE:
3490#ifdef LIBXML_SGML_ENABLED
3491 case XML_SGML_DOCUMENT_NODE:
3492#endif
3493 return(NULL);
3494 case XML_NAMESPACE_DECL:
3495 /*
3496 * TODO !!! may require extending struct _xmlNs with
3497 * parent field
3498 * C.f. Infoset case...
3499 */
3500 return(NULL);
3501 }
3502 return(NULL);
3503 }
3504 if (cur == ctxt->context->doc->children)
3505 return((xmlNodePtr) ctxt->context->doc);
3506 if (cur == (xmlNodePtr) ctxt->context->doc)
3507 return(NULL);
3508 switch (cur->type) {
3509 case XML_ELEMENT_NODE:
3510 case XML_TEXT_NODE:
3511 case XML_CDATA_SECTION_NODE:
3512 case XML_ENTITY_REF_NODE:
3513 case XML_ENTITY_NODE:
3514 case XML_PI_NODE:
3515 case XML_COMMENT_NODE:
3516 case XML_NOTATION_NODE:
3517 case XML_DTD_NODE:
3518 case XML_ELEMENT_DECL:
3519 case XML_ATTRIBUTE_DECL:
3520 case XML_ENTITY_DECL:
3521 case XML_XINCLUDE_START:
3522 case XML_XINCLUDE_END:
3523 return(cur->parent);
3524 case XML_ATTRIBUTE_NODE: {
3525 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3526
3527 return(att->parent);
3528 }
3529 case XML_DOCUMENT_NODE:
3530 case XML_DOCUMENT_TYPE_NODE:
3531 case XML_DOCUMENT_FRAG_NODE:
3532 case XML_HTML_DOCUMENT_NODE:
3533#ifdef LIBXML_SGML_ENABLED
3534 case XML_SGML_DOCUMENT_NODE:
3535#endif
3536 return(NULL);
3537 case XML_NAMESPACE_DECL:
3538 /*
3539 * TODO !!! may require extending struct _xmlNs with
3540 * parent field
3541 * C.f. Infoset case...
3542 */
3543 return(NULL);
3544 }
3545 return(NULL);
3546}
3547
3548/**
3549 * xmlXPathNextAncestorOrSelf:
3550 * @ctxt: the XPath Parser context
3551 * @cur: the current node in the traversal
3552 *
3553 * Traversal function for the "ancestor-or-self" direction
3554 * he ancestor-or-self axis contains the context node and ancestors of
3555 * the context node in reverse document order; thus the context node is
3556 * the first node on the axis, and the context node's parent the second;
3557 * parent here is defined the same as with the parent axis.
3558 *
3559 * Returns the next element following that axis
3560 */
3561xmlNodePtr
3562xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3563 if (cur == NULL)
3564 return(ctxt->context->node);
3565 return(xmlXPathNextAncestor(ctxt, cur));
3566}
3567
3568/**
3569 * xmlXPathNextFollowingSibling:
3570 * @ctxt: the XPath Parser context
3571 * @cur: the current node in the traversal
3572 *
3573 * Traversal function for the "following-sibling" direction
3574 * The following-sibling axis contains the following siblings of the context
3575 * node in document order.
3576 *
3577 * Returns the next element following that axis
3578 */
3579xmlNodePtr
3580xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3581 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3582 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3583 return(NULL);
3584 if (cur == (xmlNodePtr) ctxt->context->doc)
3585 return(NULL);
3586 if (cur == NULL)
3587 return(ctxt->context->node->next);
3588 return(cur->next);
3589}
3590
3591/**
3592 * xmlXPathNextPrecedingSibling:
3593 * @ctxt: the XPath Parser context
3594 * @cur: the current node in the traversal
3595 *
3596 * Traversal function for the "preceding-sibling" direction
3597 * The preceding-sibling axis contains the preceding siblings of the context
3598 * node in reverse document order; the first preceding sibling is first on the
3599 * axis; the sibling preceding that node is the second on the axis and so on.
3600 *
3601 * Returns the next element following that axis
3602 */
3603xmlNodePtr
3604xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3605 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3606 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3607 return(NULL);
3608 if (cur == (xmlNodePtr) ctxt->context->doc)
3609 return(NULL);
3610 if (cur == NULL)
3611 return(ctxt->context->node->prev);
3612 return(cur->prev);
3613}
3614
3615/**
3616 * xmlXPathNextFollowing:
3617 * @ctxt: the XPath Parser context
3618 * @cur: the current node in the traversal
3619 *
3620 * Traversal function for the "following" direction
3621 * The following axis contains all nodes in the same document as the context
3622 * node that are after the context node in document order, excluding any
3623 * descendants and excluding attribute nodes and namespace nodes; the nodes
3624 * are ordered in document order
3625 *
3626 * Returns the next element following that axis
3627 */
3628xmlNodePtr
3629xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3630 if (cur != NULL && cur->children != NULL)
3631 return cur->children ;
3632 if (cur == NULL) cur = ctxt->context->node;
3633 if (cur == NULL) return(NULL) ; /* ERROR */
3634 if (cur->next != NULL) return(cur->next) ;
3635 do {
3636 cur = cur->parent;
3637 if (cur == NULL) return(NULL);
3638 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3639 if (cur->next != NULL) return(cur->next);
3640 } while (cur != NULL);
3641 return(cur);
3642}
3643
3644/*
3645 * xmlXPathIsAncestor:
3646 * @ancestor: the ancestor node
3647 * @node: the current node
3648 *
3649 * Check that @ancestor is a @node's ancestor
3650 *
3651 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3652 */
3653static int
3654xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3655 if ((ancestor == NULL) || (node == NULL)) return(0);
3656 /* nodes need to be in the same document */
3657 if (ancestor->doc != node->doc) return(0);
3658 /* avoid searching if ancestor or node is the root node */
3659 if (ancestor == (xmlNodePtr) node->doc) return(1);
3660 if (node == (xmlNodePtr) ancestor->doc) return(0);
3661 while (node->parent != NULL) {
3662 if (node->parent == ancestor)
3663 return(1);
3664 node = node->parent;
3665 }
3666 return(0);
3667}
3668
3669/**
3670 * xmlXPathNextPreceding:
3671 * @ctxt: the XPath Parser context
3672 * @cur: the current node in the traversal
3673 *
3674 * Traversal function for the "preceding" direction
3675 * the preceding axis contains all nodes in the same document as the context
3676 * node that are before the context node in document order, excluding any
3677 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3678 * ordered in reverse document order
3679 *
3680 * Returns the next element following that axis
3681 */
3682xmlNodePtr
3683xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3684 if (cur == NULL)
3685 cur = ctxt->context->node ;
3686 do {
3687 if (cur->prev != NULL) {
3688 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3689 ;
3690 return(cur) ;
3691 }
3692
3693 cur = cur->parent;
3694 if (cur == NULL) return(NULL);
3695 if (cur == ctxt->context->doc->children) return(NULL);
3696 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3697 return(cur);
3698}
3699
3700/**
3701 * xmlXPathNextNamespace:
3702 * @ctxt: the XPath Parser context
3703 * @cur: the current attribute in the traversal
3704 *
3705 * Traversal function for the "namespace" direction
3706 * the namespace axis contains the namespace nodes of the context node;
3707 * the order of nodes on this axis is implementation-defined; the axis will
3708 * be empty unless the context node is an element
3709 *
3710 * Returns the next element following that axis
3711 */
3712xmlNodePtr
3713xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3714 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3715 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3716 if (ctxt->context->namespaces != NULL)
3717 xmlFree(ctxt->context->namespaces);
3718 ctxt->context->namespaces =
3719 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3720 if (ctxt->context->namespaces == NULL) return(NULL);
3721 ctxt->context->nsNr = 0;
3722 }
3723 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3724}
3725
3726/**
3727 * xmlXPathNextAttribute:
3728 * @ctxt: the XPath Parser context
3729 * @cur: the current attribute in the traversal
3730 *
3731 * Traversal function for the "attribute" direction
3732 * TODO: support DTD inherited default attributes
3733 *
3734 * Returns the next element following that axis
3735 */
3736xmlNodePtr
3737xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3738 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3739 if (cur == NULL) {
3740 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3741 return(NULL);
3742 return((xmlNodePtr)ctxt->context->node->properties);
3743 }
3744 return((xmlNodePtr)cur->next);
3745}
3746
3747/************************************************************************
3748 * *
3749 * NodeTest Functions *
3750 * *
3751 ************************************************************************/
3752
Owen Taylor3473f882001-02-23 17:55:21 +00003753#define IS_FUNCTION 200
3754
3755/**
3756 * xmlXPathNodeCollectAndTest:
3757 * @ctxt: the XPath Parser context
3758 * @axis: the XPath axis
3759 * @test: the XPath test
3760 * @type: the XPath type
3761 * @prefix: the namesapce prefix if any
3762 * @name: the name used in the search if any
3763 *
3764 * This is the function implementing a step: based on the current list
3765 * of nodes, it builds up a new list, looking at all nodes under that
3766 * axis and selecting them.
3767 *
3768 * Returns the new NodeSet resulting from the search.
3769 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003770static void
Owen Taylor3473f882001-02-23 17:55:21 +00003771xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis,
3772 xmlXPathTestVal test, xmlXPathTypeVal type,
3773 const xmlChar *prefix, const xmlChar *name) {
3774#ifdef DEBUG_STEP
3775 int n = 0, t = 0;
3776#endif
3777 int i;
3778 xmlNodeSetPtr ret;
3779 xmlXPathTraversalFunction next = NULL;
3780 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
3781 xmlNodePtr cur = NULL;
3782 xmlXPathObjectPtr obj;
3783 xmlNodeSetPtr nodelist;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003784 xmlNodePtr tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003785
3786 CHECK_TYPE(XPATH_NODESET);
3787 obj = valuePop(ctxt);
3788 addNode = xmlXPathNodeSetAdd;
3789
3790#ifdef DEBUG_STEP
3791 xmlGenericError(xmlGenericErrorContext,
3792 "new step : ");
3793#endif
3794 switch (axis) {
3795 case AXIS_ANCESTOR:
3796#ifdef DEBUG_STEP
3797 xmlGenericError(xmlGenericErrorContext,
3798 "axis 'ancestors' ");
3799#endif
3800 next = xmlXPathNextAncestor; break;
3801 case AXIS_ANCESTOR_OR_SELF:
3802#ifdef DEBUG_STEP
3803 xmlGenericError(xmlGenericErrorContext,
3804 "axis 'ancestors-or-self' ");
3805#endif
3806 next = xmlXPathNextAncestorOrSelf; break;
3807 case AXIS_ATTRIBUTE:
3808#ifdef DEBUG_STEP
3809 xmlGenericError(xmlGenericErrorContext,
3810 "axis 'attributes' ");
3811#endif
3812 next = xmlXPathNextAttribute; break;
3813 break;
3814 case AXIS_CHILD:
3815#ifdef DEBUG_STEP
3816 xmlGenericError(xmlGenericErrorContext,
3817 "axis 'child' ");
3818#endif
3819 next = xmlXPathNextChild; break;
3820 case AXIS_DESCENDANT:
3821#ifdef DEBUG_STEP
3822 xmlGenericError(xmlGenericErrorContext,
3823 "axis 'descendant' ");
3824#endif
3825 next = xmlXPathNextDescendant; break;
3826 case AXIS_DESCENDANT_OR_SELF:
3827#ifdef DEBUG_STEP
3828 xmlGenericError(xmlGenericErrorContext,
3829 "axis 'descendant-or-self' ");
3830#endif
3831 next = xmlXPathNextDescendantOrSelf; break;
3832 case AXIS_FOLLOWING:
3833#ifdef DEBUG_STEP
3834 xmlGenericError(xmlGenericErrorContext,
3835 "axis 'following' ");
3836#endif
3837 next = xmlXPathNextFollowing; break;
3838 case AXIS_FOLLOWING_SIBLING:
3839#ifdef DEBUG_STEP
3840 xmlGenericError(xmlGenericErrorContext,
3841 "axis 'following-siblings' ");
3842#endif
3843 next = xmlXPathNextFollowingSibling; break;
3844 case AXIS_NAMESPACE:
3845#ifdef DEBUG_STEP
3846 xmlGenericError(xmlGenericErrorContext,
3847 "axis 'namespace' ");
3848#endif
3849 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
3850 break;
3851 case AXIS_PARENT:
3852#ifdef DEBUG_STEP
3853 xmlGenericError(xmlGenericErrorContext,
3854 "axis 'parent' ");
3855#endif
3856 next = xmlXPathNextParent; break;
3857 case AXIS_PRECEDING:
3858#ifdef DEBUG_STEP
3859 xmlGenericError(xmlGenericErrorContext,
3860 "axis 'preceding' ");
3861#endif
3862 next = xmlXPathNextPreceding; break;
3863 case AXIS_PRECEDING_SIBLING:
3864#ifdef DEBUG_STEP
3865 xmlGenericError(xmlGenericErrorContext,
3866 "axis 'preceding-sibling' ");
3867#endif
3868 next = xmlXPathNextPrecedingSibling; break;
3869 case AXIS_SELF:
3870#ifdef DEBUG_STEP
3871 xmlGenericError(xmlGenericErrorContext,
3872 "axis 'self' ");
3873#endif
3874 next = xmlXPathNextSelf; break;
3875 }
3876 if (next == NULL)
3877 return;
3878
3879 nodelist = obj->nodesetval;
3880 if ((nodelist != NULL) &&
3881 (nodelist->nodeNr <= 1))
3882 addNode = xmlXPathNodeSetAddUnique;
3883 else
3884 addNode = xmlXPathNodeSetAdd;
3885 ret = xmlXPathNodeSetCreate(NULL);
3886#ifdef DEBUG_STEP
3887 xmlGenericError(xmlGenericErrorContext,
3888 " context contains %d nodes\n",
3889 nodelist->nodeNr);
3890 switch (test) {
3891 case NODE_TEST_NODE:
3892 xmlGenericError(xmlGenericErrorContext,
3893 " searching all nodes\n");
3894 break;
3895 case NODE_TEST_NONE:
3896 xmlGenericError(xmlGenericErrorContext,
3897 " searching for none !!!\n");
3898 break;
3899 case NODE_TEST_TYPE:
3900 xmlGenericError(xmlGenericErrorContext,
3901 " searching for type %d\n", type);
3902 break;
3903 case NODE_TEST_PI:
3904 xmlGenericError(xmlGenericErrorContext,
3905 " searching for PI !!!\n");
3906 break;
3907 case NODE_TEST_ALL:
3908 xmlGenericError(xmlGenericErrorContext,
3909 " searching for *\n");
3910 break;
3911 case NODE_TEST_NS:
3912 xmlGenericError(xmlGenericErrorContext,
3913 " searching for namespace %s\n",
3914 prefix);
3915 break;
3916 case NODE_TEST_NAME:
3917 xmlGenericError(xmlGenericErrorContext,
3918 " searching for name %s\n", name);
3919 if (prefix != NULL)
3920 xmlGenericError(xmlGenericErrorContext,
3921 " with namespace %s\n",
3922 prefix);
3923 break;
3924 }
3925 xmlGenericError(xmlGenericErrorContext, "Testing : ");
3926#endif
3927 /*
3928 * 2.3 Node Tests
3929 * - For the attribute axis, the principal node type is attribute.
3930 * - For the namespace axis, the principal node type is namespace.
3931 * - For other axes, the principal node type is element.
3932 *
3933 * A node test * is true for any node of the
3934 * principal node type. For example, child::* willi
3935 * select all element children of the context node
3936 */
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003937 tmp = ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003938 for (i = 0;i < nodelist->nodeNr; i++) {
3939 ctxt->context->node = nodelist->nodeTab[i];
3940
3941 cur = NULL;
3942 do {
3943 cur = next(ctxt, cur);
3944 if (cur == NULL) break;
3945#ifdef DEBUG_STEP
3946 t++;
3947 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
3948#endif
3949 switch (test) {
3950 case NODE_TEST_NONE:
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003951 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003952 STRANGE
3953 return;
3954 case NODE_TEST_TYPE:
3955 if ((cur->type == type) ||
3956 ((type == NODE_TYPE_NODE) &&
3957 ((cur->type == XML_DOCUMENT_NODE) ||
3958 (cur->type == XML_HTML_DOCUMENT_NODE) ||
3959 (cur->type == XML_ELEMENT_NODE) ||
3960 (cur->type == XML_PI_NODE) ||
3961 (cur->type == XML_COMMENT_NODE) ||
3962 (cur->type == XML_CDATA_SECTION_NODE) ||
3963 (cur->type == XML_TEXT_NODE)))) {
3964#ifdef DEBUG_STEP
3965 n++;
3966#endif
3967 addNode(ret, cur);
3968 }
3969 break;
3970 case NODE_TEST_PI:
3971 if (cur->type == XML_PI_NODE) {
3972 if ((name != NULL) &&
3973 (!xmlStrEqual(name, cur->name)))
3974 break;
3975#ifdef DEBUG_STEP
3976 n++;
3977#endif
3978 addNode(ret, cur);
3979 }
3980 break;
3981 case NODE_TEST_ALL:
3982 if (axis == AXIS_ATTRIBUTE) {
3983 if (cur->type == XML_ATTRIBUTE_NODE) {
3984#ifdef DEBUG_STEP
3985 n++;
3986#endif
3987 addNode(ret, cur);
3988 }
3989 } else if (axis == AXIS_NAMESPACE) {
3990 if (cur->type == XML_NAMESPACE_DECL) {
3991#ifdef DEBUG_STEP
3992 n++;
3993#endif
3994 addNode(ret, cur);
3995 }
3996 } else {
3997 if ((cur->type == XML_ELEMENT_NODE) ||
3998 (cur->type == XML_DOCUMENT_NODE) ||
3999 (cur->type == XML_HTML_DOCUMENT_NODE)) {
4000 if (prefix == NULL) {
4001#ifdef DEBUG_STEP
4002 n++;
4003#endif
4004 addNode(ret, cur);
4005 } else if ((cur->ns != NULL) &&
4006 (xmlStrEqual(prefix,
4007 cur->ns->href))) {
4008#ifdef DEBUG_STEP
4009 n++;
4010#endif
4011 addNode(ret, cur);
4012 }
4013 }
4014 }
4015 break;
4016 case NODE_TEST_NS: {
4017 TODO;
4018 break;
4019 }
4020 case NODE_TEST_NAME:
4021 switch (cur->type) {
4022 case XML_ELEMENT_NODE:
4023 if (xmlStrEqual(name, cur->name)) {
4024 if (prefix == NULL) {
4025 if ((cur->ns == NULL) ||
4026 (cur->ns->prefix == NULL)) {
4027#ifdef DEBUG_STEP
4028 n++;
4029#endif
4030 addNode(ret, cur);
4031 }
4032 } else {
4033 if ((cur->ns != NULL) &&
4034 (xmlStrEqual(prefix,
4035 cur->ns->href))) {
4036#ifdef DEBUG_STEP
4037 n++;
4038#endif
4039 addNode(ret, cur);
4040 }
4041 }
4042 }
4043 break;
4044 case XML_ATTRIBUTE_NODE: {
4045 xmlAttrPtr attr = (xmlAttrPtr) cur;
4046 if (xmlStrEqual(name, attr->name)) {
4047 if (prefix == NULL) {
4048 if ((attr->ns == NULL) ||
4049 (attr->ns->prefix == NULL)) {
4050#ifdef DEBUG_STEP
4051 n++;
4052#endif
4053 addNode(ret, (xmlNodePtr) attr);
4054 }
4055 } else {
4056 if ((attr->ns != NULL) &&
4057 (xmlStrEqual(prefix,
4058 attr->ns->href))) {
4059#ifdef DEBUG_STEP
4060 n++;
4061#endif
4062 addNode(ret, (xmlNodePtr) attr);
4063 }
4064 }
4065 }
4066 break;
4067 }
4068 case XML_NAMESPACE_DECL: {
4069 TODO;
4070 break;
4071 }
4072 default:
4073 break;
4074 }
4075 break;
4076 }
4077 } while (cur != NULL);
4078 }
Bjorn Reesee1dc0112001-03-03 12:09:03 +00004079 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004080#ifdef DEBUG_STEP
4081 xmlGenericError(xmlGenericErrorContext,
4082 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
4083#endif
4084 xmlXPathFreeObject(obj);
4085 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
4086}
4087
4088
4089/************************************************************************
4090 * *
4091 * Implicit tree core function library *
4092 * *
4093 ************************************************************************/
4094
4095/**
4096 * xmlXPathRoot:
4097 * @ctxt: the XPath Parser context
4098 *
4099 * Initialize the context to the root of the document
4100 */
4101void
4102xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
4103 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
4104 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4105}
4106
4107/************************************************************************
4108 * *
4109 * The explicit core function library *
4110 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
4111 * *
4112 ************************************************************************/
4113
4114
4115/**
4116 * xmlXPathLastFunction:
4117 * @ctxt: the XPath Parser context
4118 * @nargs: the number of arguments
4119 *
4120 * Implement the last() XPath function
4121 * number last()
4122 * The last function returns the number of nodes in the context node list.
4123 */
4124void
4125xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4126 CHECK_ARITY(0);
4127 if (ctxt->context->contextSize >= 0) {
4128 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
4129#ifdef DEBUG_EXPR
4130 xmlGenericError(xmlGenericErrorContext,
4131 "last() : %d\n", ctxt->context->contextSize);
4132#endif
4133 } else {
4134 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
4135 }
4136}
4137
4138/**
4139 * xmlXPathPositionFunction:
4140 * @ctxt: the XPath Parser context
4141 * @nargs: the number of arguments
4142 *
4143 * Implement the position() XPath function
4144 * number position()
4145 * The position function returns the position of the context node in the
4146 * context node list. The first position is 1, and so the last positionr
4147 * will be equal to last().
4148 */
4149void
4150xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4151 CHECK_ARITY(0);
4152 if (ctxt->context->proximityPosition >= 0) {
4153 valuePush(ctxt,
4154 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
4155#ifdef DEBUG_EXPR
4156 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
4157 ctxt->context->proximityPosition);
4158#endif
4159 } else {
4160 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
4161 }
4162}
4163
4164/**
4165 * xmlXPathCountFunction:
4166 * @ctxt: the XPath Parser context
4167 * @nargs: the number of arguments
4168 *
4169 * Implement the count() XPath function
4170 * number count(node-set)
4171 */
4172void
4173xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4174 xmlXPathObjectPtr cur;
4175
4176 CHECK_ARITY(1);
4177 if ((ctxt->value == NULL) ||
4178 ((ctxt->value->type != XPATH_NODESET) &&
4179 (ctxt->value->type != XPATH_XSLT_TREE)))
4180 XP_ERROR(XPATH_INVALID_TYPE);
4181 cur = valuePop(ctxt);
4182
4183 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
4184 xmlXPathFreeObject(cur);
4185}
4186
4187/**
4188 * xmlXPathIdFunction:
4189 * @ctxt: the XPath Parser context
4190 * @nargs: the number of arguments
4191 *
4192 * Implement the id() XPath function
4193 * node-set id(object)
4194 * The id function selects elements by their unique ID
4195 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
4196 * then the result is the union of the result of applying id to the
4197 * string value of each of the nodes in the argument node-set. When the
4198 * argument to id is of any other type, the argument is converted to a
4199 * string as if by a call to the string function; the string is split
4200 * into a whitespace-separated list of tokens (whitespace is any sequence
4201 * of characters matching the production S); the result is a node-set
4202 * containing the elements in the same document as the context node that
4203 * have a unique ID equal to any of the tokens in the list.
4204 */
4205void
4206xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4207 const xmlChar *tokens;
4208 const xmlChar *cur;
4209 xmlChar *ID;
4210 xmlAttrPtr attr;
4211 xmlNodePtr elem = NULL;
4212 xmlXPathObjectPtr ret, obj;
4213
4214 CHECK_ARITY(1);
4215 obj = valuePop(ctxt);
4216 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
4217 if (obj->type == XPATH_NODESET) {
4218 xmlXPathObjectPtr newobj;
4219 int i;
4220
4221 ret = xmlXPathNewNodeSet(NULL);
4222
4223 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
4224 valuePush(ctxt,
4225 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
4226 xmlXPathStringFunction(ctxt, 1);
4227 xmlXPathIdFunction(ctxt, 1);
4228 newobj = valuePop(ctxt);
4229 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
4230 newobj->nodesetval);
4231 xmlXPathFreeObject(newobj);
4232 }
4233
4234 xmlXPathFreeObject(obj);
4235 valuePush(ctxt, ret);
4236 return;
4237 }
4238 if (obj->type != XPATH_STRING) {
4239 valuePush(ctxt, obj);
4240 xmlXPathStringFunction(ctxt, 1);
4241 obj = valuePop(ctxt);
4242 if (obj->type != XPATH_STRING) {
4243 xmlXPathFreeObject(obj);
4244 return;
4245 }
4246 }
4247 tokens = obj->stringval;
4248
4249 ret = xmlXPathNewNodeSet(NULL);
4250 valuePush(ctxt, ret);
4251 if (tokens == NULL) {
4252 xmlXPathFreeObject(obj);
4253 return;
4254 }
4255
4256 cur = tokens;
4257
4258 while (IS_BLANK(*cur)) cur++;
4259 while (*cur != 0) {
4260 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
4261 (*cur == '.') || (*cur == '-') ||
4262 (*cur == '_') || (*cur == ':') ||
4263 (IS_COMBINING(*cur)) ||
4264 (IS_EXTENDER(*cur)))
4265 cur++;
4266
4267 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
4268
4269 ID = xmlStrndup(tokens, cur - tokens);
4270 attr = xmlGetID(ctxt->context->doc, ID);
4271 if (attr != NULL) {
4272 elem = attr->parent;
4273 xmlXPathNodeSetAdd(ret->nodesetval, elem);
4274 }
4275 if (ID != NULL)
4276 xmlFree(ID);
4277
4278 while (IS_BLANK(*cur)) cur++;
4279 tokens = cur;
4280 }
4281 xmlXPathFreeObject(obj);
4282 return;
4283}
4284
4285/**
4286 * xmlXPathLocalNameFunction:
4287 * @ctxt: the XPath Parser context
4288 * @nargs: the number of arguments
4289 *
4290 * Implement the local-name() XPath function
4291 * string local-name(node-set?)
4292 * The local-name function returns a string containing the local part
4293 * of the name of the node in the argument node-set that is first in
4294 * document order. If the node-set is empty or the first node has no
4295 * name, an empty string is returned. If the argument is omitted it
4296 * defaults to the context node.
4297 */
4298void
4299xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4300 xmlXPathObjectPtr cur;
4301
4302 if (nargs == 0) {
4303 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4304 nargs = 1;
4305 }
4306
4307 CHECK_ARITY(1);
4308 if ((ctxt->value == NULL) ||
4309 ((ctxt->value->type != XPATH_NODESET) &&
4310 (ctxt->value->type != XPATH_XSLT_TREE)))
4311 XP_ERROR(XPATH_INVALID_TYPE);
4312 cur = valuePop(ctxt);
4313
4314 if (cur->nodesetval->nodeNr == 0) {
4315 valuePush(ctxt, xmlXPathNewCString(""));
4316 } else {
4317 int i = 0; /* Should be first in document order !!!!! */
4318 switch (cur->nodesetval->nodeTab[i]->type) {
4319 case XML_ELEMENT_NODE:
4320 case XML_ATTRIBUTE_NODE:
4321 case XML_PI_NODE:
4322 valuePush(ctxt,
4323 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4324 break;
4325 case XML_NAMESPACE_DECL:
4326 valuePush(ctxt, xmlXPathNewString(
4327 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4328 break;
4329 default:
4330 valuePush(ctxt, xmlXPathNewCString(""));
4331 }
4332 }
4333 xmlXPathFreeObject(cur);
4334}
4335
4336/**
4337 * xmlXPathNamespaceURIFunction:
4338 * @ctxt: the XPath Parser context
4339 * @nargs: the number of arguments
4340 *
4341 * Implement the namespace-uri() XPath function
4342 * string namespace-uri(node-set?)
4343 * The namespace-uri function returns a string containing the
4344 * namespace URI of the expanded name of the node in the argument
4345 * node-set that is first in document order. If the node-set is empty,
4346 * the first node has no name, or the expanded name has no namespace
4347 * URI, an empty string is returned. If the argument is omitted it
4348 * defaults to the context node.
4349 */
4350void
4351xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4352 xmlXPathObjectPtr cur;
4353
4354 if (nargs == 0) {
4355 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4356 nargs = 1;
4357 }
4358 CHECK_ARITY(1);
4359 if ((ctxt->value == NULL) ||
4360 ((ctxt->value->type != XPATH_NODESET) &&
4361 (ctxt->value->type != XPATH_XSLT_TREE)))
4362 XP_ERROR(XPATH_INVALID_TYPE);
4363 cur = valuePop(ctxt);
4364
4365 if (cur->nodesetval->nodeNr == 0) {
4366 valuePush(ctxt, xmlXPathNewCString(""));
4367 } else {
4368 int i = 0; /* Should be first in document order !!!!! */
4369 switch (cur->nodesetval->nodeTab[i]->type) {
4370 case XML_ELEMENT_NODE:
4371 case XML_ATTRIBUTE_NODE:
4372 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4373 valuePush(ctxt, xmlXPathNewCString(""));
4374 else
4375 valuePush(ctxt, xmlXPathNewString(
4376 cur->nodesetval->nodeTab[i]->ns->href));
4377 break;
4378 default:
4379 valuePush(ctxt, xmlXPathNewCString(""));
4380 }
4381 }
4382 xmlXPathFreeObject(cur);
4383}
4384
4385/**
4386 * xmlXPathNameFunction:
4387 * @ctxt: the XPath Parser context
4388 * @nargs: the number of arguments
4389 *
4390 * Implement the name() XPath function
4391 * string name(node-set?)
4392 * The name function returns a string containing a QName representing
4393 * the name of the node in the argument node-set that is first in documenti
4394 * order. The QName must represent the name with respect to the namespace
4395 * declarations in effect on the node whose name is being represented.
4396 * Typically, this will be the form in which the name occurred in the XML
4397 * source. This need not be the case if there are namespace declarations
4398 * in effect on the node that associate multiple prefixes with the same
4399 * namespace. However, an implementation may include information about
4400 * the original prefix in its representation of nodes; in this case, an
4401 * implementation can ensure that the returned string is always the same
4402 * as the QName used in the XML source. If the argument it omitted it
4403 * defaults to the context node.
4404 * Libxml keep the original prefix so the "real qualified name" used is
4405 * returned.
4406 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004407static void
Owen Taylor3473f882001-02-23 17:55:21 +00004408xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4409 xmlXPathObjectPtr cur;
4410
4411 if (nargs == 0) {
4412 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4413 nargs = 1;
4414 }
4415
4416 CHECK_ARITY(1);
4417 if ((ctxt->value == NULL) ||
4418 ((ctxt->value->type != XPATH_NODESET) &&
4419 (ctxt->value->type != XPATH_XSLT_TREE)))
4420 XP_ERROR(XPATH_INVALID_TYPE);
4421 cur = valuePop(ctxt);
4422
4423 if (cur->nodesetval->nodeNr == 0) {
4424 valuePush(ctxt, xmlXPathNewCString(""));
4425 } else {
4426 int i = 0; /* Should be first in document order !!!!! */
4427
4428 switch (cur->nodesetval->nodeTab[i]->type) {
4429 case XML_ELEMENT_NODE:
4430 case XML_ATTRIBUTE_NODE:
4431 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4432 valuePush(ctxt, xmlXPathNewString(
4433 cur->nodesetval->nodeTab[i]->name));
4434
4435 else {
4436 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004437 snprintf(name, sizeof(name), "%s:%s",
4438 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4439 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004440 name[sizeof(name) - 1] = 0;
4441 valuePush(ctxt, xmlXPathNewCString(name));
4442 }
4443 break;
4444 default:
4445 valuePush(ctxt,
4446 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4447 xmlXPathLocalNameFunction(ctxt, 1);
4448 }
4449 }
4450 xmlXPathFreeObject(cur);
4451}
4452
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004453
4454/**
4455 * xmlXPathConvertString:
4456 * @val: an XPath object
4457 *
4458 * Converts an existing object to its string() equivalent
4459 *
4460 * Returns the new object, the old one is freed (or the operation
4461 * is done directly on @val)
4462 */
4463xmlXPathObjectPtr
4464xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004465 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004466
4467 if (val == NULL)
4468 return(xmlXPathNewCString(""));
4469 switch (val->type) {
4470 case XPATH_UNDEFINED:
4471#ifdef DEBUG_EXPR
4472 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4473#endif
4474 ret = xmlXPathNewCString("");
4475 break;
4476 case XPATH_XSLT_TREE:
4477 case XPATH_NODESET:
4478 if (val->nodesetval == NULL)
4479 ret = xmlXPathNewCString("");
4480 else if (val->nodesetval->nodeNr == 0) {
4481 ret = xmlXPathNewCString("");
4482 } else {
4483 xmlChar *res;
4484
4485 xmlXPathNodeSetSort(val->nodesetval);
4486 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4487 /* TODO: avoid allocating res to free it */
4488 ret = xmlXPathNewString(res);
4489 if (res != NULL)
4490 xmlFree(res);
4491 }
4492 break;
4493 case XPATH_STRING:
4494 return(val);
4495 case XPATH_BOOLEAN:
4496 if (val->boolval) ret = xmlXPathNewCString("true");
4497 else ret = xmlXPathNewCString("false");
4498 break;
4499 case XPATH_NUMBER: {
4500 char buf[100];
4501
4502 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4503 ret = xmlXPathNewCString(buf);
4504 break;
4505 }
4506 case XPATH_USERS:
4507 case XPATH_POINT:
4508 case XPATH_RANGE:
4509 case XPATH_LOCATIONSET:
4510 TODO
4511 ret = xmlXPathNewCString("");
4512 break;
4513 }
4514 xmlXPathFreeObject(val);
4515 return(ret);
4516}
4517
Owen Taylor3473f882001-02-23 17:55:21 +00004518/**
4519 * xmlXPathStringFunction:
4520 * @ctxt: the XPath Parser context
4521 * @nargs: the number of arguments
4522 *
4523 * Implement the string() XPath function
4524 * string string(object?)
4525 * he string function converts an object to a string as follows:
4526 * - A node-set is converted to a string by returning the value of
4527 * the node in the node-set that is first in document order.
4528 * If the node-set is empty, an empty string is returned.
4529 * - A number is converted to a string as follows
4530 * + NaN is converted to the string NaN
4531 * + positive zero is converted to the string 0
4532 * + negative zero is converted to the string 0
4533 * + positive infinity is converted to the string Infinity
4534 * + negative infinity is converted to the string -Infinity
4535 * + if the number is an integer, the number is represented in
4536 * decimal form as a Number with no decimal point and no leading
4537 * zeros, preceded by a minus sign (-) if the number is negative
4538 * + otherwise, the number is represented in decimal form as a
4539 * Number including a decimal point with at least one digit
4540 * before the decimal point and at least one digit after the
4541 * decimal point, preceded by a minus sign (-) if the number
4542 * is negative; there must be no leading zeros before the decimal
4543 * point apart possibly from the one required digit immediatelyi
4544 * before the decimal point; beyond the one required digit
4545 * after the decimal point there must be as many, but only as
4546 * many, more digits as are needed to uniquely distinguish the
4547 * number from all other IEEE 754 numeric values.
4548 * - The boolean false value is converted to the string false.
4549 * The boolean true value is converted to the string true.
4550 *
4551 * If the argument is omitted, it defaults to a node-set with the
4552 * context node as its only member.
4553 */
4554void
4555xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4556 xmlXPathObjectPtr cur;
4557
4558 if (nargs == 0) {
4559 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4560 nargs = 1;
4561 }
4562
4563 CHECK_ARITY(1);
4564 cur = valuePop(ctxt);
4565 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004566 cur = xmlXPathConvertString(cur);
4567 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004568}
4569
4570/**
4571 * xmlXPathStringLengthFunction:
4572 * @ctxt: the XPath Parser context
4573 * @nargs: the number of arguments
4574 *
4575 * Implement the string-length() XPath function
4576 * number string-length(string?)
4577 * The string-length returns the number of characters in the string
4578 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4579 * the context node converted to a string, in other words the value
4580 * of the context node.
4581 */
4582void
4583xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4584 xmlXPathObjectPtr cur;
4585
4586 if (nargs == 0) {
4587 if (ctxt->context->node == NULL) {
4588 valuePush(ctxt, xmlXPathNewFloat(0));
4589 } else {
4590 xmlChar *content;
4591
4592 content = xmlNodeGetContent(ctxt->context->node);
4593 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
4594 xmlFree(content);
4595 }
4596 return;
4597 }
4598 CHECK_ARITY(1);
4599 CAST_TO_STRING;
4600 CHECK_TYPE(XPATH_STRING);
4601 cur = valuePop(ctxt);
4602 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
4603 xmlXPathFreeObject(cur);
4604}
4605
4606/**
4607 * xmlXPathConcatFunction:
4608 * @ctxt: the XPath Parser context
4609 * @nargs: the number of arguments
4610 *
4611 * Implement the concat() XPath function
4612 * string concat(string, string, string*)
4613 * The concat function returns the concatenation of its arguments.
4614 */
4615void
4616xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4617 xmlXPathObjectPtr cur, newobj;
4618 xmlChar *tmp;
4619
4620 if (nargs < 2) {
4621 CHECK_ARITY(2);
4622 }
4623
4624 CAST_TO_STRING;
4625 cur = valuePop(ctxt);
4626 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4627 xmlXPathFreeObject(cur);
4628 return;
4629 }
4630 nargs--;
4631
4632 while (nargs > 0) {
4633 CAST_TO_STRING;
4634 newobj = valuePop(ctxt);
4635 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4636 xmlXPathFreeObject(newobj);
4637 xmlXPathFreeObject(cur);
4638 XP_ERROR(XPATH_INVALID_TYPE);
4639 }
4640 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4641 newobj->stringval = cur->stringval;
4642 cur->stringval = tmp;
4643
4644 xmlXPathFreeObject(newobj);
4645 nargs--;
4646 }
4647 valuePush(ctxt, cur);
4648}
4649
4650/**
4651 * xmlXPathContainsFunction:
4652 * @ctxt: the XPath Parser context
4653 * @nargs: the number of arguments
4654 *
4655 * Implement the contains() XPath function
4656 * boolean contains(string, string)
4657 * The contains function returns true if the first argument string
4658 * contains the second argument string, and otherwise returns false.
4659 */
4660void
4661xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4662 xmlXPathObjectPtr hay, needle;
4663
4664 CHECK_ARITY(2);
4665 CAST_TO_STRING;
4666 CHECK_TYPE(XPATH_STRING);
4667 needle = valuePop(ctxt);
4668 CAST_TO_STRING;
4669 hay = valuePop(ctxt);
4670 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4671 xmlXPathFreeObject(hay);
4672 xmlXPathFreeObject(needle);
4673 XP_ERROR(XPATH_INVALID_TYPE);
4674 }
4675 if (xmlStrstr(hay->stringval, needle->stringval))
4676 valuePush(ctxt, xmlXPathNewBoolean(1));
4677 else
4678 valuePush(ctxt, xmlXPathNewBoolean(0));
4679 xmlXPathFreeObject(hay);
4680 xmlXPathFreeObject(needle);
4681}
4682
4683/**
4684 * xmlXPathStartsWithFunction:
4685 * @ctxt: the XPath Parser context
4686 * @nargs: the number of arguments
4687 *
4688 * Implement the starts-with() XPath function
4689 * boolean starts-with(string, string)
4690 * The starts-with function returns true if the first argument string
4691 * starts with the second argument string, and otherwise returns false.
4692 */
4693void
4694xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4695 xmlXPathObjectPtr hay, needle;
4696 int n;
4697
4698 CHECK_ARITY(2);
4699 CAST_TO_STRING;
4700 CHECK_TYPE(XPATH_STRING);
4701 needle = valuePop(ctxt);
4702 CAST_TO_STRING;
4703 hay = valuePop(ctxt);
4704 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4705 xmlXPathFreeObject(hay);
4706 xmlXPathFreeObject(needle);
4707 XP_ERROR(XPATH_INVALID_TYPE);
4708 }
4709 n = xmlStrlen(needle->stringval);
4710 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4711 valuePush(ctxt, xmlXPathNewBoolean(0));
4712 else
4713 valuePush(ctxt, xmlXPathNewBoolean(1));
4714 xmlXPathFreeObject(hay);
4715 xmlXPathFreeObject(needle);
4716}
4717
4718/**
4719 * xmlXPathSubstringFunction:
4720 * @ctxt: the XPath Parser context
4721 * @nargs: the number of arguments
4722 *
4723 * Implement the substring() XPath function
4724 * string substring(string, number, number?)
4725 * The substring function returns the substring of the first argument
4726 * starting at the position specified in the second argument with
4727 * length specified in the third argument. For example,
4728 * substring("12345",2,3) returns "234". If the third argument is not
4729 * specified, it returns the substring starting at the position specified
4730 * in the second argument and continuing to the end of the string. For
4731 * example, substring("12345",2) returns "2345". More precisely, each
4732 * character in the string (see [3.6 Strings]) is considered to have a
4733 * numeric position: the position of the first character is 1, the position
4734 * of the second character is 2 and so on. The returned substring contains
4735 * those characters for which the position of the character is greater than
4736 * or equal to the second argument and, if the third argument is specified,
4737 * less than the sum of the second and third arguments; the comparisons
4738 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4739 * - substring("12345", 1.5, 2.6) returns "234"
4740 * - substring("12345", 0, 3) returns "12"
4741 * - substring("12345", 0 div 0, 3) returns ""
4742 * - substring("12345", 1, 0 div 0) returns ""
4743 * - substring("12345", -42, 1 div 0) returns "12345"
4744 * - substring("12345", -1 div 0, 1 div 0) returns ""
4745 */
4746void
4747xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4748 xmlXPathObjectPtr str, start, len;
4749 double le, in;
4750 int i, l;
4751 xmlChar *ret;
4752
4753 /*
4754 * Conformance needs to be checked !!!!!
4755 */
4756 if (nargs < 2) {
4757 CHECK_ARITY(2);
4758 }
4759 if (nargs > 3) {
4760 CHECK_ARITY(3);
4761 }
4762 if (nargs == 3) {
4763 CAST_TO_NUMBER;
4764 CHECK_TYPE(XPATH_NUMBER);
4765 len = valuePop(ctxt);
4766 le = len->floatval;
4767 xmlXPathFreeObject(len);
4768 } else {
4769 le = 2000000000;
4770 }
4771 CAST_TO_NUMBER;
4772 CHECK_TYPE(XPATH_NUMBER);
4773 start = valuePop(ctxt);
4774 in = start->floatval;
4775 xmlXPathFreeObject(start);
4776 CAST_TO_STRING;
4777 CHECK_TYPE(XPATH_STRING);
4778 str = valuePop(ctxt);
4779 le += in;
4780
4781 /* integer index of the first char */
4782 i = (int) in;
4783 if (((double)i) != in) i++;
4784
4785 /* integer index of the last char */
4786 l = (int) le;
4787 if (((double)l) != le) l++;
4788
4789 /* back to a zero based len */
4790 i--;
4791 l--;
4792
4793 /* check against the string len */
4794 if (l > 1024) {
4795 l = xmlStrlen(str->stringval);
4796 }
4797 if (i < 0) {
4798 i = 0;
4799 }
4800
4801 /* number of chars to copy */
4802 l -= i;
4803
4804 ret = xmlStrsub(str->stringval, i, l);
4805 if (ret == NULL)
4806 valuePush(ctxt, xmlXPathNewCString(""));
4807 else {
4808 valuePush(ctxt, xmlXPathNewString(ret));
4809 xmlFree(ret);
4810 }
4811 xmlXPathFreeObject(str);
4812}
4813
4814/**
4815 * xmlXPathSubstringBeforeFunction:
4816 * @ctxt: the XPath Parser context
4817 * @nargs: the number of arguments
4818 *
4819 * Implement the substring-before() XPath function
4820 * string substring-before(string, string)
4821 * The substring-before function returns the substring of the first
4822 * argument string that precedes the first occurrence of the second
4823 * argument string in the first argument string, or the empty string
4824 * if the first argument string does not contain the second argument
4825 * string. For example, substring-before("1999/04/01","/") returns 1999.
4826 */
4827void
4828xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4829 xmlXPathObjectPtr str;
4830 xmlXPathObjectPtr find;
4831 xmlBufferPtr target;
4832 const xmlChar *point;
4833 int offset;
4834
4835 CHECK_ARITY(2);
4836 CAST_TO_STRING;
4837 find = valuePop(ctxt);
4838 CAST_TO_STRING;
4839 str = valuePop(ctxt);
4840
4841 target = xmlBufferCreate();
4842 if (target) {
4843 point = xmlStrstr(str->stringval, find->stringval);
4844 if (point) {
4845 offset = (int)(point - str->stringval);
4846 xmlBufferAdd(target, str->stringval, offset);
4847 }
4848 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4849 xmlBufferFree(target);
4850 }
4851
4852 xmlXPathFreeObject(str);
4853 xmlXPathFreeObject(find);
4854}
4855
4856/**
4857 * xmlXPathSubstringAfterFunction:
4858 * @ctxt: the XPath Parser context
4859 * @nargs: the number of arguments
4860 *
4861 * Implement the substring-after() XPath function
4862 * string substring-after(string, string)
4863 * The substring-after function returns the substring of the first
4864 * argument string that follows the first occurrence of the second
4865 * argument string in the first argument string, or the empty stringi
4866 * if the first argument string does not contain the second argument
4867 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4868 * and substring-after("1999/04/01","19") returns 99/04/01.
4869 */
4870void
4871xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4872 xmlXPathObjectPtr str;
4873 xmlXPathObjectPtr find;
4874 xmlBufferPtr target;
4875 const xmlChar *point;
4876 int offset;
4877
4878 CHECK_ARITY(2);
4879 CAST_TO_STRING;
4880 find = valuePop(ctxt);
4881 CAST_TO_STRING;
4882 str = valuePop(ctxt);
4883
4884 target = xmlBufferCreate();
4885 if (target) {
4886 point = xmlStrstr(str->stringval, find->stringval);
4887 if (point) {
4888 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4889 xmlBufferAdd(target, &str->stringval[offset],
4890 xmlStrlen(str->stringval) - offset);
4891 }
4892 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4893 xmlBufferFree(target);
4894 }
4895
4896 xmlXPathFreeObject(str);
4897 xmlXPathFreeObject(find);
4898}
4899
4900/**
4901 * xmlXPathNormalizeFunction:
4902 * @ctxt: the XPath Parser context
4903 * @nargs: the number of arguments
4904 *
4905 * Implement the normalize-space() XPath function
4906 * string normalize-space(string?)
4907 * The normalize-space function returns the argument string with white
4908 * space normalized by stripping leading and trailing whitespace
4909 * and replacing sequences of whitespace characters by a single
4910 * space. Whitespace characters are the same allowed by the S production
4911 * in XML. If the argument is omitted, it defaults to the context
4912 * node converted to a string, in other words the value of the context node.
4913 */
4914void
4915xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4916 xmlXPathObjectPtr obj = NULL;
4917 xmlChar *source = NULL;
4918 xmlBufferPtr target;
4919 xmlChar blank;
4920
4921 if (nargs == 0) {
4922 /* Use current context node */
4923 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4924 xmlXPathStringFunction(ctxt, 1);
4925 nargs = 1;
4926 }
4927
4928 CHECK_ARITY(1);
4929 CAST_TO_STRING;
4930 CHECK_TYPE(XPATH_STRING);
4931 obj = valuePop(ctxt);
4932 source = obj->stringval;
4933
4934 target = xmlBufferCreate();
4935 if (target && source) {
4936
4937 /* Skip leading whitespaces */
4938 while (IS_BLANK(*source))
4939 source++;
4940
4941 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4942 blank = 0;
4943 while (*source) {
4944 if (IS_BLANK(*source)) {
4945 blank = *source;
4946 } else {
4947 if (blank) {
4948 xmlBufferAdd(target, &blank, 1);
4949 blank = 0;
4950 }
4951 xmlBufferAdd(target, source, 1);
4952 }
4953 source++;
4954 }
4955
4956 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4957 xmlBufferFree(target);
4958 }
4959 xmlXPathFreeObject(obj);
4960}
4961
4962/**
4963 * xmlXPathTranslateFunction:
4964 * @ctxt: the XPath Parser context
4965 * @nargs: the number of arguments
4966 *
4967 * Implement the translate() XPath function
4968 * string translate(string, string, string)
4969 * The translate function returns the first argument string with
4970 * occurrences of characters in the second argument string replaced
4971 * by the character at the corresponding position in the third argument
4972 * string. For example, translate("bar","abc","ABC") returns the string
4973 * BAr. If there is a character in the second argument string with no
4974 * character at a corresponding position in the third argument string
4975 * (because the second argument string is longer than the third argument
4976 * string), then occurrences of that character in the first argument
4977 * string are removed. For example, translate("--aaa--","abc-","ABC")
4978 * returns "AAA". If a character occurs more than once in second
4979 * argument string, then the first occurrence determines the replacement
4980 * character. If the third argument string is longer than the second
4981 * argument string, then excess characters are ignored.
4982 */
4983void
4984xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4985 xmlXPathObjectPtr str;
4986 xmlXPathObjectPtr from;
4987 xmlXPathObjectPtr to;
4988 xmlBufferPtr target;
4989 int i, offset, max;
4990 xmlChar ch;
4991 const xmlChar *point;
4992
4993 CHECK_ARITY(3);
4994
4995 CAST_TO_STRING;
4996 to = valuePop(ctxt);
4997 CAST_TO_STRING;
4998 from = valuePop(ctxt);
4999 CAST_TO_STRING;
5000 str = valuePop(ctxt);
5001
5002 target = xmlBufferCreate();
5003 if (target) {
5004 max = xmlStrlen(to->stringval);
5005 for (i = 0; (ch = str->stringval[i]); i++) {
5006 point = xmlStrchr(from->stringval, ch);
5007 if (point) {
5008 /* Warning: This may not work with UTF-8 */
5009 offset = (int)(point - from->stringval);
5010 if (offset < max)
5011 xmlBufferAdd(target, &to->stringval[offset], 1);
5012 } else
5013 xmlBufferAdd(target, &ch, 1);
5014 }
5015 }
5016 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5017 xmlBufferFree(target);
5018 xmlXPathFreeObject(str);
5019 xmlXPathFreeObject(from);
5020 xmlXPathFreeObject(to);
5021}
5022
5023/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005024 * xmlXPathConvertBoolean:
5025 * @val: an XPath object
5026 *
5027 * Converts an existing object to its boolean() equivalent
5028 *
5029 * Returns the new object, the old one is freed (or the operation
5030 * is done directly on @val)
5031 */
5032xmlXPathObjectPtr
5033xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
5034 int res = 0;
5035
5036 if (val == NULL)
5037 return(NULL);
5038 switch (val->type) {
5039 case XPATH_NODESET:
5040 case XPATH_XSLT_TREE:
5041 if ((val->nodesetval == NULL) ||
5042 (val->nodesetval->nodeNr == 0)) res = 0;
5043 else
5044 res = 1;
5045 break;
5046 case XPATH_STRING:
5047 if ((val->stringval == NULL) ||
5048 (val->stringval[0] == 0)) res = 0;
5049 else
5050 res = 1;
5051 break;
5052 case XPATH_BOOLEAN:
5053 return(val);
5054 case XPATH_NUMBER:
5055 if (val->floatval) res = 1;
5056 break;
5057 default:
5058 STRANGE
5059 }
5060 xmlXPathFreeObject(val);
5061 return(xmlXPathNewBoolean(res));
5062}
5063
5064/**
Owen Taylor3473f882001-02-23 17:55:21 +00005065 * xmlXPathBooleanFunction:
5066 * @ctxt: the XPath Parser context
5067 * @nargs: the number of arguments
5068 *
5069 * Implement the boolean() XPath function
5070 * boolean boolean(object)
5071 * he boolean function converts its argument to a boolean as follows:
5072 * - a number is true if and only if it is neither positive or
5073 * negative zero nor NaN
5074 * - a node-set is true if and only if it is non-empty
5075 * - a string is true if and only if its length is non-zero
5076 */
5077void
5078xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5079 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00005080
5081 CHECK_ARITY(1);
5082 cur = valuePop(ctxt);
5083 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005084 cur = xmlXPathConvertBoolean(cur);
5085 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005086}
5087
5088/**
5089 * xmlXPathNotFunction:
5090 * @ctxt: the XPath Parser context
5091 * @nargs: the number of arguments
5092 *
5093 * Implement the not() XPath function
5094 * boolean not(boolean)
5095 * The not function returns true if its argument is false,
5096 * and false otherwise.
5097 */
5098void
5099xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5100 CHECK_ARITY(1);
5101 CAST_TO_BOOLEAN;
5102 CHECK_TYPE(XPATH_BOOLEAN);
5103 ctxt->value->boolval = ! ctxt->value->boolval;
5104}
5105
5106/**
5107 * xmlXPathTrueFunction:
5108 * @ctxt: the XPath Parser context
5109 * @nargs: the number of arguments
5110 *
5111 * Implement the true() XPath function
5112 * boolean true()
5113 */
5114void
5115xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5116 CHECK_ARITY(0);
5117 valuePush(ctxt, xmlXPathNewBoolean(1));
5118}
5119
5120/**
5121 * xmlXPathFalseFunction:
5122 * @ctxt: the XPath Parser context
5123 * @nargs: the number of arguments
5124 *
5125 * Implement the false() XPath function
5126 * boolean false()
5127 */
5128void
5129xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5130 CHECK_ARITY(0);
5131 valuePush(ctxt, xmlXPathNewBoolean(0));
5132}
5133
5134/**
5135 * xmlXPathLangFunction:
5136 * @ctxt: the XPath Parser context
5137 * @nargs: the number of arguments
5138 *
5139 * Implement the lang() XPath function
5140 * boolean lang(string)
5141 * The lang function returns true or false depending on whether the
5142 * language of the context node as specified by xml:lang attributes
5143 * is the same as or is a sublanguage of the language specified by
5144 * the argument string. The language of the context node is determined
5145 * by the value of the xml:lang attribute on the context node, or, if
5146 * the context node has no xml:lang attribute, by the value of the
5147 * xml:lang attribute on the nearest ancestor of the context node that
5148 * has an xml:lang attribute. If there is no such attribute, then lang
5149 * returns false. If there is such an attribute, then lang returns
5150 * true if the attribute value is equal to the argument ignoring case,
5151 * or if there is some suffix starting with - such that the attribute
5152 * value is equal to the argument ignoring that suffix of the attribute
5153 * value and ignoring case.
5154 */
5155void
5156xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5157 xmlXPathObjectPtr val;
5158 const xmlChar *theLang;
5159 const xmlChar *lang;
5160 int ret = 0;
5161 int i;
5162
5163 CHECK_ARITY(1);
5164 CAST_TO_STRING;
5165 CHECK_TYPE(XPATH_STRING);
5166 val = valuePop(ctxt);
5167 lang = val->stringval;
5168 theLang = xmlNodeGetLang(ctxt->context->node);
5169 if ((theLang != NULL) && (lang != NULL)) {
5170 for (i = 0;lang[i] != 0;i++)
5171 if (toupper(lang[i]) != toupper(theLang[i]))
5172 goto not_equal;
5173 ret = 1;
5174 }
5175not_equal:
5176 xmlXPathFreeObject(val);
5177 valuePush(ctxt, xmlXPathNewBoolean(ret));
5178}
5179
5180/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005181 * xmlXPathConvertNumber:
5182 * @val: an XPath object
5183 *
5184 * Converts an existing object to its number() equivalent
5185 *
5186 * Returns the new object, the old one is freed (or the operation
5187 * is done directly on @val)
5188 */
5189xmlXPathObjectPtr
5190xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005191 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005192 double res;
5193
5194 if (val == NULL)
5195 return(xmlXPathNewFloat(0.0));
5196 switch (val->type) {
5197 case XPATH_UNDEFINED:
5198#ifdef DEBUG_EXPR
5199 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5200#endif
5201 ret = xmlXPathNewFloat(0.0);
5202 break;
5203 case XPATH_XSLT_TREE:
5204 case XPATH_NODESET:
5205 val = xmlXPathConvertString(val);
5206 /* no break on purpose */
5207 case XPATH_STRING:
5208 res = xmlXPathStringEvalNumber(val->stringval);
5209 ret = xmlXPathNewFloat(res);
5210 break;
5211 case XPATH_BOOLEAN:
5212 if (val->boolval) ret = xmlXPathNewFloat(1.0);
5213 else ret = xmlXPathNewFloat(0.0);
5214 break;
5215 case XPATH_NUMBER:
5216 return(val);
5217 case XPATH_USERS:
5218 case XPATH_POINT:
5219 case XPATH_RANGE:
5220 case XPATH_LOCATIONSET:
5221 TODO
5222 ret = xmlXPathNewFloat(0.0);
5223 break;
5224 }
5225 xmlXPathFreeObject(val);
5226 return(ret);
5227}
5228
5229/**
Owen Taylor3473f882001-02-23 17:55:21 +00005230 * xmlXPathNumberFunction:
5231 * @ctxt: the XPath Parser context
5232 * @nargs: the number of arguments
5233 *
5234 * Implement the number() XPath function
5235 * number number(object?)
5236 */
5237void
5238xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5239 xmlXPathObjectPtr cur;
5240 double res;
5241
5242 if (nargs == 0) {
5243 if (ctxt->context->node == NULL) {
5244 valuePush(ctxt, xmlXPathNewFloat(0.0));
5245 } else {
5246 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
5247
5248 res = xmlXPathStringEvalNumber(content);
5249 valuePush(ctxt, xmlXPathNewFloat(res));
5250 xmlFree(content);
5251 }
5252 return;
5253 }
5254
5255 CHECK_ARITY(1);
5256 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005257 cur = xmlXPathConvertNumber(cur);
5258 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005259}
5260
5261/**
5262 * xmlXPathSumFunction:
5263 * @ctxt: the XPath Parser context
5264 * @nargs: the number of arguments
5265 *
5266 * Implement the sum() XPath function
5267 * number sum(node-set)
5268 * The sum function returns the sum of the values of the nodes in
5269 * the argument node-set.
5270 */
5271void
5272xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5273 xmlXPathObjectPtr cur;
5274 int i;
5275
5276 CHECK_ARITY(1);
5277 if ((ctxt->value == NULL) ||
5278 ((ctxt->value->type != XPATH_NODESET) &&
5279 (ctxt->value->type != XPATH_XSLT_TREE)))
5280 XP_ERROR(XPATH_INVALID_TYPE);
5281 cur = valuePop(ctxt);
5282
5283 if (cur->nodesetval->nodeNr == 0) {
5284 valuePush(ctxt, xmlXPathNewFloat(0.0));
5285 } else {
5286 valuePush(ctxt,
5287 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
5288 xmlXPathNumberFunction(ctxt, 1);
5289 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
5290 valuePush(ctxt,
5291 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5292 xmlXPathAddValues(ctxt);
5293 }
5294 }
5295 xmlXPathFreeObject(cur);
5296}
5297
5298/**
5299 * xmlXPathFloorFunction:
5300 * @ctxt: the XPath Parser context
5301 * @nargs: the number of arguments
5302 *
5303 * Implement the floor() XPath function
5304 * number floor(number)
5305 * The floor function returns the largest (closest to positive infinity)
5306 * number that is not greater than the argument and that is an integer.
5307 */
5308void
5309xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5310 CHECK_ARITY(1);
5311 CAST_TO_NUMBER;
5312 CHECK_TYPE(XPATH_NUMBER);
5313#if 0
5314 ctxt->value->floatval = floor(ctxt->value->floatval);
5315#else
5316 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5317 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5318#endif
5319}
5320
5321/**
5322 * xmlXPathCeilingFunction:
5323 * @ctxt: the XPath Parser context
5324 * @nargs: the number of arguments
5325 *
5326 * Implement the ceiling() XPath function
5327 * number ceiling(number)
5328 * The ceiling function returns the smallest (closest to negative infinity)
5329 * number that is not less than the argument and that is an integer.
5330 */
5331void
5332xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5333 double f;
5334
5335 CHECK_ARITY(1);
5336 CAST_TO_NUMBER;
5337 CHECK_TYPE(XPATH_NUMBER);
5338
5339#if 0
5340 ctxt->value->floatval = ceil(ctxt->value->floatval);
5341#else
5342 f = (double)((int) ctxt->value->floatval);
5343 if (f != ctxt->value->floatval)
5344 ctxt->value->floatval = f + 1;
5345#endif
5346}
5347
5348/**
5349 * xmlXPathRoundFunction:
5350 * @ctxt: the XPath Parser context
5351 * @nargs: the number of arguments
5352 *
5353 * Implement the round() XPath function
5354 * number round(number)
5355 * The round function returns the number that is closest to the
5356 * argument and that is an integer. If there are two such numbers,
5357 * then the one that is even is returned.
5358 */
5359void
5360xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5361 double f;
5362
5363 CHECK_ARITY(1);
5364 CAST_TO_NUMBER;
5365 CHECK_TYPE(XPATH_NUMBER);
5366
5367 if ((ctxt->value->floatval == xmlXPathNAN) ||
5368 (ctxt->value->floatval == xmlXPathPINF) ||
5369 (ctxt->value->floatval == xmlXPathNINF) ||
5370 (ctxt->value->floatval == 0.0))
5371 return;
5372
5373#if 0
5374 f = floor(ctxt->value->floatval);
5375#else
5376 f = (double)((int) ctxt->value->floatval);
5377#endif
5378 if (ctxt->value->floatval < f + 0.5)
5379 ctxt->value->floatval = f;
5380 else
5381 ctxt->value->floatval = f + 1;
5382}
5383
5384/************************************************************************
5385 * *
5386 * The Parser *
5387 * *
5388 ************************************************************************/
5389
5390/*
5391 * a couple of forward declarations since we use a recursive call based
5392 * implementation.
5393 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005394static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
5395static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt);
5396static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005397#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005398static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5399#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005400#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005401static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005402#endif
5403
5404/**
5405 * xmlXPathParseNCName:
5406 * @ctxt: the XPath Parser context
5407 *
5408 * parse an XML namespace non qualified name.
5409 *
5410 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5411 *
5412 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5413 * CombiningChar | Extender
5414 *
5415 * Returns the namespace name or NULL
5416 */
5417
5418xmlChar *
5419xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5420 const xmlChar *q;
5421 xmlChar *ret = NULL;
5422
5423 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5424 q = NEXT;
5425
5426 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5427 (CUR == '.') || (CUR == '-') ||
5428 (CUR == '_') ||
5429 (IS_COMBINING(CUR)) ||
5430 (IS_EXTENDER(CUR)))
5431 NEXT;
5432
5433 ret = xmlStrndup(q, CUR_PTR - q);
5434
5435 return(ret);
5436}
5437
5438/**
5439 * xmlXPathParseQName:
5440 * @ctxt: the XPath Parser context
5441 * @prefix: a xmlChar **
5442 *
5443 * parse an XML qualified name
5444 *
5445 * [NS 5] QName ::= (Prefix ':')? LocalPart
5446 *
5447 * [NS 6] Prefix ::= NCName
5448 *
5449 * [NS 7] LocalPart ::= NCName
5450 *
5451 * Returns the function returns the local part, and prefix is updated
5452 * to get the Prefix if any.
5453 */
5454
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005455static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005456xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5457 xmlChar *ret = NULL;
5458
5459 *prefix = NULL;
5460 ret = xmlXPathParseNCName(ctxt);
5461 if (CUR == ':') {
5462 *prefix = ret;
5463 NEXT;
5464 ret = xmlXPathParseNCName(ctxt);
5465 }
5466 return(ret);
5467}
5468
5469/**
5470 * xmlXPathParseName:
5471 * @ctxt: the XPath Parser context
5472 *
5473 * parse an XML name
5474 *
5475 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5476 * CombiningChar | Extender
5477 *
5478 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5479 *
5480 * Returns the namespace name or NULL
5481 */
5482
5483xmlChar *
5484xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5485 const xmlChar *q;
5486 xmlChar *ret = NULL;
5487
5488 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5489 q = NEXT;
5490
5491 /* TODO Make this UTF8 compliant !!! */
5492 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5493 (CUR == '.') || (CUR == '-') ||
5494 (CUR == '_') || (CUR == ':') ||
5495 (IS_COMBINING(CUR)) ||
5496 (IS_EXTENDER(CUR)))
5497 NEXT;
5498
5499 ret = xmlStrndup(q, CUR_PTR - q);
5500
5501 return(ret);
5502}
5503
5504/**
5505 * xmlXPathStringEvalNumber:
5506 * @str: A string to scan
5507 *
5508 * [30] Number ::= Digits ('.' Digits?)?
5509 * | '.' Digits
5510 * [31] Digits ::= [0-9]+
5511 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005512 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005513 * In complement of the Number expression, this function also handles
5514 * negative values : '-' Number.
5515 *
5516 * Returns the double value.
5517 */
5518double
5519xmlXPathStringEvalNumber(const xmlChar *str) {
5520 const xmlChar *cur = str;
5521 double ret = 0.0;
5522 double mult = 1;
5523 int ok = 0;
5524 int isneg = 0;
5525
5526 while (IS_BLANK(*cur)) cur++;
5527 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5528 return(xmlXPathNAN);
5529 }
5530 if (*cur == '-') {
5531 isneg = 1;
5532 cur++;
5533 }
5534 while ((*cur >= '0') && (*cur <= '9')) {
5535 ret = ret * 10 + (*cur - '0');
5536 ok = 1;
5537 cur++;
5538 }
5539 if (*cur == '.') {
5540 cur++;
5541 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5542 return(xmlXPathNAN);
5543 }
5544 while ((*cur >= '0') && (*cur <= '9')) {
5545 mult /= 10;
5546 ret = ret + (*cur - '0') * mult;
5547 cur++;
5548 }
5549 }
5550 while (IS_BLANK(*cur)) cur++;
5551 if (*cur != 0) return(xmlXPathNAN);
5552 if (isneg) ret = -ret;
5553 return(ret);
5554}
5555
5556/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005557 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005558 * @ctxt: the XPath Parser context
5559 *
5560 * [30] Number ::= Digits ('.' Digits?)?
5561 * | '.' Digits
5562 * [31] Digits ::= [0-9]+
5563 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005564 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005565 *
5566 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005567static void
5568xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005569 double ret = 0.0;
5570 double mult = 1;
5571 int ok = 0;
5572
5573 CHECK_ERROR;
5574 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5575 XP_ERROR(XPATH_NUMBER_ERROR);
5576 }
5577 while ((CUR >= '0') && (CUR <= '9')) {
5578 ret = ret * 10 + (CUR - '0');
5579 ok = 1;
5580 NEXT;
5581 }
5582 if (CUR == '.') {
5583 NEXT;
5584 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5585 XP_ERROR(XPATH_NUMBER_ERROR);
5586 }
5587 while ((CUR >= '0') && (CUR <= '9')) {
5588 mult /= 10;
5589 ret = ret + (CUR - '0') * mult;
5590 NEXT;
5591 }
5592 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005593 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5594 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005595}
5596
5597/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005598 * xmlXPathParseLiteral:
5599 * @ctxt: the XPath Parser context
5600 *
5601 * Parse a Literal
5602 *
5603 * [29] Literal ::= '"' [^"]* '"'
5604 * | "'" [^']* "'"
5605 *
5606 * Returns the value found or NULL in case of error
5607 */
5608static xmlChar *
5609xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5610 const xmlChar *q;
5611 xmlChar *ret = NULL;
5612
5613 if (CUR == '"') {
5614 NEXT;
5615 q = CUR_PTR;
5616 while ((IS_CHAR(CUR)) && (CUR != '"'))
5617 NEXT;
5618 if (!IS_CHAR(CUR)) {
5619 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5620 } else {
5621 ret = xmlStrndup(q, CUR_PTR - q);
5622 NEXT;
5623 }
5624 } else if (CUR == '\'') {
5625 NEXT;
5626 q = CUR_PTR;
5627 while ((IS_CHAR(CUR)) && (CUR != '\''))
5628 NEXT;
5629 if (!IS_CHAR(CUR)) {
5630 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5631 } else {
5632 ret = xmlStrndup(q, CUR_PTR - q);
5633 NEXT;
5634 }
5635 } else {
5636 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5637 }
5638 return(ret);
5639}
5640
5641/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005642 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005643 * @ctxt: the XPath Parser context
5644 *
5645 * Parse a Literal and push it on the stack.
5646 *
5647 * [29] Literal ::= '"' [^"]* '"'
5648 * | "'" [^']* "'"
5649 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005650 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005651 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005652static void
5653xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005654 const xmlChar *q;
5655 xmlChar *ret = NULL;
5656
5657 if (CUR == '"') {
5658 NEXT;
5659 q = CUR_PTR;
5660 while ((IS_CHAR(CUR)) && (CUR != '"'))
5661 NEXT;
5662 if (!IS_CHAR(CUR)) {
5663 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5664 } else {
5665 ret = xmlStrndup(q, CUR_PTR - q);
5666 NEXT;
5667 }
5668 } else if (CUR == '\'') {
5669 NEXT;
5670 q = CUR_PTR;
5671 while ((IS_CHAR(CUR)) && (CUR != '\''))
5672 NEXT;
5673 if (!IS_CHAR(CUR)) {
5674 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5675 } else {
5676 ret = xmlStrndup(q, CUR_PTR - q);
5677 NEXT;
5678 }
5679 } else {
5680 XP_ERROR(XPATH_START_LITERAL_ERROR);
5681 }
5682 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005683 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5684 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005685 xmlFree(ret);
5686}
5687
5688/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005689 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005690 * @ctxt: the XPath Parser context
5691 *
5692 * Parse a VariableReference, evaluate it and push it on the stack.
5693 *
5694 * The variable bindings consist of a mapping from variable names
5695 * to variable values. The value of a variable is an object, which
5696 * of any of the types that are possible for the value of an expression,
5697 * and may also be of additional types not specified here.
5698 *
5699 * Early evaluation is possible since:
5700 * The variable bindings [...] used to evaluate a subexpression are
5701 * always the same as those used to evaluate the containing expression.
5702 *
5703 * [36] VariableReference ::= '$' QName
5704 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005705static void
5706xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005707 xmlChar *name;
5708 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005709
5710 SKIP_BLANKS;
5711 if (CUR != '$') {
5712 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5713 }
5714 NEXT;
5715 name = xmlXPathParseQName(ctxt, &prefix);
5716 if (name == NULL) {
5717 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5718 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005719 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005720 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5721 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005722 SKIP_BLANKS;
5723}
5724
5725/**
5726 * xmlXPathIsNodeType:
5727 * @ctxt: the XPath Parser context
5728 * @name: a name string
5729 *
5730 * Is the name given a NodeType one.
5731 *
5732 * [38] NodeType ::= 'comment'
5733 * | 'text'
5734 * | 'processing-instruction'
5735 * | 'node'
5736 *
5737 * Returns 1 if true 0 otherwise
5738 */
5739int
5740xmlXPathIsNodeType(const xmlChar *name) {
5741 if (name == NULL)
5742 return(0);
5743
5744 if (xmlStrEqual(name, BAD_CAST "comment"))
5745 return(1);
5746 if (xmlStrEqual(name, BAD_CAST "text"))
5747 return(1);
5748 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5749 return(1);
5750 if (xmlStrEqual(name, BAD_CAST "node"))
5751 return(1);
5752 return(0);
5753}
5754
5755/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005756 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005757 * @ctxt: the XPath Parser context
5758 *
5759 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5760 * [17] Argument ::= Expr
5761 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005762 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005763 * pushed on the stack
5764 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005765static void
5766xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005767 xmlChar *name;
5768 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005769 int nbargs = 0;
5770
5771 name = xmlXPathParseQName(ctxt, &prefix);
5772 if (name == NULL) {
5773 XP_ERROR(XPATH_EXPR_ERROR);
5774 }
5775 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005776#ifdef DEBUG_EXPR
5777 if (prefix == NULL)
5778 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5779 name);
5780 else
5781 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5782 prefix, name);
5783#endif
5784
Owen Taylor3473f882001-02-23 17:55:21 +00005785 if (CUR != '(') {
5786 XP_ERROR(XPATH_EXPR_ERROR);
5787 }
5788 NEXT;
5789 SKIP_BLANKS;
5790
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005791 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005792 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005793 int op1 = ctxt->comp->last;
5794 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005795 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005796 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005797 nbargs++;
5798 if (CUR == ')') break;
5799 if (CUR != ',') {
5800 XP_ERROR(XPATH_EXPR_ERROR);
5801 }
5802 NEXT;
5803 SKIP_BLANKS;
5804 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005805 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5806 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005807 NEXT;
5808 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005809}
5810
5811/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005812 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005813 * @ctxt: the XPath Parser context
5814 *
5815 * [15] PrimaryExpr ::= VariableReference
5816 * | '(' Expr ')'
5817 * | Literal
5818 * | Number
5819 * | FunctionCall
5820 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005821 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005822 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005823static void
5824xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005825 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005826 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005827 else if (CUR == '(') {
5828 NEXT;
5829 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005830 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005831 if (CUR != ')') {
5832 XP_ERROR(XPATH_EXPR_ERROR);
5833 }
5834 NEXT;
5835 SKIP_BLANKS;
5836 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005837 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005838 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005839 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005840 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005841 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005842 }
5843 SKIP_BLANKS;
5844}
5845
5846/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005847 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005848 * @ctxt: the XPath Parser context
5849 *
5850 * [20] FilterExpr ::= PrimaryExpr
5851 * | FilterExpr Predicate
5852 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005853 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005854 * Square brackets are used to filter expressions in the same way that
5855 * they are used in location paths. It is an error if the expression to
5856 * be filtered does not evaluate to a node-set. The context node list
5857 * used for evaluating the expression in square brackets is the node-set
5858 * to be filtered listed in document order.
5859 */
5860
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005861static void
5862xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5863 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005864 CHECK_ERROR;
5865 SKIP_BLANKS;
5866
5867 while (CUR == '[') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005868 xmlXPathCompPredicate(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005869 SKIP_BLANKS;
5870 }
5871
5872
5873}
5874
5875/**
5876 * xmlXPathScanName:
5877 * @ctxt: the XPath Parser context
5878 *
5879 * Trickery: parse an XML name but without consuming the input flow
5880 * Needed to avoid insanity in the parser state.
5881 *
5882 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5883 * CombiningChar | Extender
5884 *
5885 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5886 *
5887 * [6] Names ::= Name (S Name)*
5888 *
5889 * Returns the Name parsed or NULL
5890 */
5891
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005892static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005893xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5894 xmlChar buf[XML_MAX_NAMELEN];
5895 int len = 0;
5896
5897 SKIP_BLANKS;
5898 if (!IS_LETTER(CUR) && (CUR != '_') &&
5899 (CUR != ':')) {
5900 return(NULL);
5901 }
5902
5903 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5904 (NXT(len) == '.') || (NXT(len) == '-') ||
5905 (NXT(len) == '_') || (NXT(len) == ':') ||
5906 (IS_COMBINING(NXT(len))) ||
5907 (IS_EXTENDER(NXT(len)))) {
5908 buf[len] = NXT(len);
5909 len++;
5910 if (len >= XML_MAX_NAMELEN) {
5911 xmlGenericError(xmlGenericErrorContext,
5912 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5913 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5914 (NXT(len) == '.') || (NXT(len) == '-') ||
5915 (NXT(len) == '_') || (NXT(len) == ':') ||
5916 (IS_COMBINING(NXT(len))) ||
5917 (IS_EXTENDER(NXT(len))))
5918 len++;
5919 break;
5920 }
5921 }
5922 return(xmlStrndup(buf, len));
5923}
5924
5925/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005926 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005927 * @ctxt: the XPath Parser context
5928 *
5929 * [19] PathExpr ::= LocationPath
5930 * | FilterExpr
5931 * | FilterExpr '/' RelativeLocationPath
5932 * | FilterExpr '//' RelativeLocationPath
5933 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005934 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005935 * The / operator and // operators combine an arbitrary expression
5936 * and a relative location path. It is an error if the expression
5937 * does not evaluate to a node-set.
5938 * The / operator does composition in the same way as when / is
5939 * used in a location path. As in location paths, // is short for
5940 * /descendant-or-self::node()/.
5941 */
5942
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005943static void
5944xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005945 int lc = 1; /* Should we branch to LocationPath ? */
5946 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5947
5948 SKIP_BLANKS;
5949 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5950 (CUR == '\'') || (CUR == '"')) {
5951 lc = 0;
5952 } else if (CUR == '*') {
5953 /* relative or absolute location path */
5954 lc = 1;
5955 } else if (CUR == '/') {
5956 /* relative or absolute location path */
5957 lc = 1;
5958 } else if (CUR == '@') {
5959 /* relative abbreviated attribute location path */
5960 lc = 1;
5961 } else if (CUR == '.') {
5962 /* relative abbreviated attribute location path */
5963 lc = 1;
5964 } else {
5965 /*
5966 * Problem is finding if we have a name here whether it's:
5967 * - a nodetype
5968 * - a function call in which case it's followed by '('
5969 * - an axis in which case it's followed by ':'
5970 * - a element name
5971 * We do an a priori analysis here rather than having to
5972 * maintain parsed token content through the recursive function
5973 * calls. This looks uglier but makes the code quite easier to
5974 * read/write/debug.
5975 */
5976 SKIP_BLANKS;
5977 name = xmlXPathScanName(ctxt);
5978 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5979#ifdef DEBUG_STEP
5980 xmlGenericError(xmlGenericErrorContext,
5981 "PathExpr: Axis\n");
5982#endif
5983 lc = 1;
5984 xmlFree(name);
5985 } else if (name != NULL) {
5986 int len =xmlStrlen(name);
5987 int blank = 0;
5988
5989
5990 while (NXT(len) != 0) {
5991 if (NXT(len) == '/') {
5992 /* element name */
5993#ifdef DEBUG_STEP
5994 xmlGenericError(xmlGenericErrorContext,
5995 "PathExpr: AbbrRelLocation\n");
5996#endif
5997 lc = 1;
5998 break;
5999 } else if (IS_BLANK(NXT(len))) {
6000 /* skip to next */
6001 blank = 1;
6002 } else if (NXT(len) == ':') {
6003#ifdef DEBUG_STEP
6004 xmlGenericError(xmlGenericErrorContext,
6005 "PathExpr: AbbrRelLocation\n");
6006#endif
6007 lc = 1;
6008 break;
6009 } else if ((NXT(len) == '(')) {
6010 /* Note Type or Function */
6011 if (xmlXPathIsNodeType(name)) {
6012#ifdef DEBUG_STEP
6013 xmlGenericError(xmlGenericErrorContext,
6014 "PathExpr: Type search\n");
6015#endif
6016 lc = 1;
6017 } else {
6018#ifdef DEBUG_STEP
6019 xmlGenericError(xmlGenericErrorContext,
6020 "PathExpr: function call\n");
6021#endif
6022 lc = 0;
6023 }
6024 break;
6025 } else if ((NXT(len) == '[')) {
6026 /* element name */
6027#ifdef DEBUG_STEP
6028 xmlGenericError(xmlGenericErrorContext,
6029 "PathExpr: AbbrRelLocation\n");
6030#endif
6031 lc = 1;
6032 break;
6033 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
6034 (NXT(len) == '=')) {
6035 lc = 1;
6036 break;
6037 } else {
6038 lc = 1;
6039 break;
6040 }
6041 len++;
6042 }
6043 if (NXT(len) == 0) {
6044#ifdef DEBUG_STEP
6045 xmlGenericError(xmlGenericErrorContext,
6046 "PathExpr: AbbrRelLocation\n");
6047#endif
6048 /* element name */
6049 lc = 1;
6050 }
6051 xmlFree(name);
6052 } else {
6053 /* make sure all cases are covered explicitely */
6054 XP_ERROR(XPATH_EXPR_ERROR);
6055 }
6056 }
6057
6058 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006059 if (CUR == '/') {
6060 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
6061 } else {
6062 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006063 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006064 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006065 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006066 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006067 CHECK_ERROR;
6068 if ((CUR == '/') && (NXT(1) == '/')) {
6069 SKIP(2);
6070 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006071
6072 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6073 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6074 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
6075
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006076 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006077 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006078 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006079 }
6080 }
6081 SKIP_BLANKS;
6082}
6083
6084/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006085 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006086 * @ctxt: the XPath Parser context
6087 *
6088 * [18] UnionExpr ::= PathExpr
6089 * | UnionExpr '|' PathExpr
6090 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006091 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006092 */
6093
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006094static void
6095xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
6096 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006097 CHECK_ERROR;
6098 SKIP_BLANKS;
6099 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006100 int op1 = ctxt->comp->last;
6101 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006102
6103 NEXT;
6104 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006105 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006106
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006107 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
6108
Owen Taylor3473f882001-02-23 17:55:21 +00006109 SKIP_BLANKS;
6110 }
Owen Taylor3473f882001-02-23 17:55:21 +00006111}
6112
6113/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006114 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006115 * @ctxt: the XPath Parser context
6116 *
6117 * [27] UnaryExpr ::= UnionExpr
6118 * | '-' UnaryExpr
6119 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006120 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006121 */
6122
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006123static void
6124xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006125 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006126 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006127
6128 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00006129 while (CUR == '-') {
6130 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006131 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006132 NEXT;
6133 SKIP_BLANKS;
6134 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006135
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006136 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006137 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006138 if (found) {
6139 if (minus)
6140 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
6141 else
6142 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006143 }
6144}
6145
6146/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006147 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006148 * @ctxt: the XPath Parser context
6149 *
6150 * [26] MultiplicativeExpr ::= UnaryExpr
6151 * | MultiplicativeExpr MultiplyOperator UnaryExpr
6152 * | MultiplicativeExpr 'div' UnaryExpr
6153 * | MultiplicativeExpr 'mod' UnaryExpr
6154 * [34] MultiplyOperator ::= '*'
6155 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006156 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006157 */
6158
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006159static void
6160xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
6161 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006162 CHECK_ERROR;
6163 SKIP_BLANKS;
6164 while ((CUR == '*') ||
6165 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
6166 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
6167 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006168 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006169
6170 if (CUR == '*') {
6171 op = 0;
6172 NEXT;
6173 } else if (CUR == 'd') {
6174 op = 1;
6175 SKIP(3);
6176 } else if (CUR == 'm') {
6177 op = 2;
6178 SKIP(3);
6179 }
6180 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006181 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006182 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006183 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006184 SKIP_BLANKS;
6185 }
6186}
6187
6188/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006189 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006190 * @ctxt: the XPath Parser context
6191 *
6192 * [25] AdditiveExpr ::= MultiplicativeExpr
6193 * | AdditiveExpr '+' MultiplicativeExpr
6194 * | AdditiveExpr '-' MultiplicativeExpr
6195 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006196 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006197 */
6198
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006199static void
6200xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006201
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006202 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006203 CHECK_ERROR;
6204 SKIP_BLANKS;
6205 while ((CUR == '+') || (CUR == '-')) {
6206 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006207 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006208
6209 if (CUR == '+') plus = 1;
6210 else plus = 0;
6211 NEXT;
6212 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006213 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006214 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006215 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006216 SKIP_BLANKS;
6217 }
6218}
6219
6220/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006221 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006222 * @ctxt: the XPath Parser context
6223 *
6224 * [24] RelationalExpr ::= AdditiveExpr
6225 * | RelationalExpr '<' AdditiveExpr
6226 * | RelationalExpr '>' AdditiveExpr
6227 * | RelationalExpr '<=' AdditiveExpr
6228 * | RelationalExpr '>=' AdditiveExpr
6229 *
6230 * A <= B > C is allowed ? Answer from James, yes with
6231 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
6232 * which is basically what got implemented.
6233 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006234 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00006235 * on the stack
6236 */
6237
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006238static void
6239xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
6240 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006241 CHECK_ERROR;
6242 SKIP_BLANKS;
6243 while ((CUR == '<') ||
6244 (CUR == '>') ||
6245 ((CUR == '<') && (NXT(1) == '=')) ||
6246 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006247 int inf, strict;
6248 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006249
6250 if (CUR == '<') inf = 1;
6251 else inf = 0;
6252 if (NXT(1) == '=') strict = 0;
6253 else strict = 1;
6254 NEXT;
6255 if (!strict) NEXT;
6256 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006257 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006258 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006259 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00006260 SKIP_BLANKS;
6261 }
6262}
6263
6264/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006265 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006266 * @ctxt: the XPath Parser context
6267 *
6268 * [23] EqualityExpr ::= RelationalExpr
6269 * | EqualityExpr '=' RelationalExpr
6270 * | EqualityExpr '!=' RelationalExpr
6271 *
6272 * A != B != C is allowed ? Answer from James, yes with
6273 * (RelationalExpr = RelationalExpr) = RelationalExpr
6274 * (RelationalExpr != RelationalExpr) != RelationalExpr
6275 * which is basically what got implemented.
6276 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006277 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006278 *
6279 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006280static void
6281xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
6282 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006283 CHECK_ERROR;
6284 SKIP_BLANKS;
6285 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006286 int eq;
6287 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006288
6289 if (CUR == '=') eq = 1;
6290 else eq = 0;
6291 NEXT;
6292 if (!eq) NEXT;
6293 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006294 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006295 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006296 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006297 SKIP_BLANKS;
6298 }
6299}
6300
6301/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006302 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006303 * @ctxt: the XPath Parser context
6304 *
6305 * [22] AndExpr ::= EqualityExpr
6306 * | AndExpr 'and' EqualityExpr
6307 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006308 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006309 *
6310 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006311static void
6312xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6313 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006314 CHECK_ERROR;
6315 SKIP_BLANKS;
6316 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006317 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006318 SKIP(3);
6319 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006320 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006321 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006322 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006323 SKIP_BLANKS;
6324 }
6325}
6326
6327/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006328 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006329 * @ctxt: the XPath Parser context
6330 *
6331 * [14] Expr ::= OrExpr
6332 * [21] OrExpr ::= AndExpr
6333 * | OrExpr 'or' AndExpr
6334 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006335 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006336 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006337static void
6338xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6339 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006340 CHECK_ERROR;
6341 SKIP_BLANKS;
6342 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006343 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006344 SKIP(2);
6345 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006346 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006347 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006348 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6349 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006350 SKIP_BLANKS;
6351 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006352 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6353 /* more ops could be optimized too */
6354 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6355 }
Owen Taylor3473f882001-02-23 17:55:21 +00006356}
6357
6358/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006359 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006360 * @ctxt: the XPath Parser context
6361 *
6362 * [8] Predicate ::= '[' PredicateExpr ']'
6363 * [9] PredicateExpr ::= Expr
6364 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006365 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006366 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006367static void
6368xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006369 int op1 = ctxt->comp->last;
6370
6371 SKIP_BLANKS;
6372 if (CUR != '[') {
6373 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6374 }
6375 NEXT;
6376 SKIP_BLANKS;
6377
6378 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006379 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006380 CHECK_ERROR;
6381
6382 if (CUR != ']') {
6383 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6384 }
6385
6386 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
6387
6388 NEXT;
6389 SKIP_BLANKS;
6390}
6391
6392/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006393 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006394 * @ctxt: the XPath Parser context
6395 * @test: pointer to a xmlXPathTestVal
6396 * @type: pointer to a xmlXPathTypeVal
6397 * @prefix: placeholder for a possible name prefix
6398 *
6399 * [7] NodeTest ::= NameTest
6400 * | NodeType '(' ')'
6401 * | 'processing-instruction' '(' Literal ')'
6402 *
6403 * [37] NameTest ::= '*'
6404 * | NCName ':' '*'
6405 * | QName
6406 * [38] NodeType ::= 'comment'
6407 * | 'text'
6408 * | 'processing-instruction'
6409 * | 'node'
6410 *
6411 * Returns the name found and update @test, @type and @prefix appropriately
6412 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006413static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006414xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6415 xmlXPathTypeVal *type, const xmlChar **prefix,
6416 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006417 int blanks;
6418
6419 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6420 STRANGE;
6421 return(NULL);
6422 }
6423 *type = 0;
6424 *test = 0;
6425 *prefix = NULL;
6426 SKIP_BLANKS;
6427
6428 if ((name == NULL) && (CUR == '*')) {
6429 /*
6430 * All elements
6431 */
6432 NEXT;
6433 *test = NODE_TEST_ALL;
6434 return(NULL);
6435 }
6436
6437 if (name == NULL)
6438 name = xmlXPathParseNCName(ctxt);
6439 if (name == NULL) {
6440 XP_ERROR0(XPATH_EXPR_ERROR);
6441 }
6442
6443 blanks = IS_BLANK(CUR);
6444 SKIP_BLANKS;
6445 if (CUR == '(') {
6446 NEXT;
6447 /*
6448 * NodeType or PI search
6449 */
6450 if (xmlStrEqual(name, BAD_CAST "comment"))
6451 *type = NODE_TYPE_COMMENT;
6452 else if (xmlStrEqual(name, BAD_CAST "node"))
6453 *type = NODE_TYPE_NODE;
6454 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6455 *type = NODE_TYPE_PI;
6456 else if (xmlStrEqual(name, BAD_CAST "text"))
6457 *type = NODE_TYPE_TEXT;
6458 else {
6459 if (name != NULL)
6460 xmlFree(name);
6461 XP_ERROR0(XPATH_EXPR_ERROR);
6462 }
6463
6464 *test = NODE_TEST_TYPE;
6465
6466 SKIP_BLANKS;
6467 if (*type == NODE_TYPE_PI) {
6468 /*
6469 * Specific case: search a PI by name.
6470 */
Owen Taylor3473f882001-02-23 17:55:21 +00006471 if (name != NULL)
6472 xmlFree(name);
6473
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006474 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006475 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006476 SKIP_BLANKS;
6477 }
6478 if (CUR != ')') {
6479 if (name != NULL)
6480 xmlFree(name);
6481 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6482 }
6483 NEXT;
6484 return(name);
6485 }
6486 *test = NODE_TEST_NAME;
6487 if ((!blanks) && (CUR == ':')) {
6488 NEXT;
6489
6490 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006491 * Since currently the parser context don't have a
6492 * namespace list associated:
6493 * The namespace name for this prefix can be computed
6494 * only at evaluation time. The compilation is done
6495 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006496 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006497#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006498 *prefix = xmlXPathNsLookup(ctxt->context, name);
6499 if (name != NULL)
6500 xmlFree(name);
6501 if (*prefix == NULL) {
6502 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6503 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006504#else
6505 *prefix = name;
6506#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006507
6508 if (CUR == '*') {
6509 /*
6510 * All elements
6511 */
6512 NEXT;
6513 *test = NODE_TEST_ALL;
6514 return(NULL);
6515 }
6516
6517 name = xmlXPathParseNCName(ctxt);
6518 if (name == NULL) {
6519 XP_ERROR0(XPATH_EXPR_ERROR);
6520 }
6521 }
6522 return(name);
6523}
6524
6525/**
6526 * xmlXPathIsAxisName:
6527 * @name: a preparsed name token
6528 *
6529 * [6] AxisName ::= 'ancestor'
6530 * | 'ancestor-or-self'
6531 * | 'attribute'
6532 * | 'child'
6533 * | 'descendant'
6534 * | 'descendant-or-self'
6535 * | 'following'
6536 * | 'following-sibling'
6537 * | 'namespace'
6538 * | 'parent'
6539 * | 'preceding'
6540 * | 'preceding-sibling'
6541 * | 'self'
6542 *
6543 * Returns the axis or 0
6544 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006545static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006546xmlXPathIsAxisName(const xmlChar *name) {
6547 xmlXPathAxisVal ret = 0;
6548 switch (name[0]) {
6549 case 'a':
6550 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6551 ret = AXIS_ANCESTOR;
6552 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6553 ret = AXIS_ANCESTOR_OR_SELF;
6554 if (xmlStrEqual(name, BAD_CAST "attribute"))
6555 ret = AXIS_ATTRIBUTE;
6556 break;
6557 case 'c':
6558 if (xmlStrEqual(name, BAD_CAST "child"))
6559 ret = AXIS_CHILD;
6560 break;
6561 case 'd':
6562 if (xmlStrEqual(name, BAD_CAST "descendant"))
6563 ret = AXIS_DESCENDANT;
6564 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6565 ret = AXIS_DESCENDANT_OR_SELF;
6566 break;
6567 case 'f':
6568 if (xmlStrEqual(name, BAD_CAST "following"))
6569 ret = AXIS_FOLLOWING;
6570 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6571 ret = AXIS_FOLLOWING_SIBLING;
6572 break;
6573 case 'n':
6574 if (xmlStrEqual(name, BAD_CAST "namespace"))
6575 ret = AXIS_NAMESPACE;
6576 break;
6577 case 'p':
6578 if (xmlStrEqual(name, BAD_CAST "parent"))
6579 ret = AXIS_PARENT;
6580 if (xmlStrEqual(name, BAD_CAST "preceding"))
6581 ret = AXIS_PRECEDING;
6582 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6583 ret = AXIS_PRECEDING_SIBLING;
6584 break;
6585 case 's':
6586 if (xmlStrEqual(name, BAD_CAST "self"))
6587 ret = AXIS_SELF;
6588 break;
6589 }
6590 return(ret);
6591}
6592
6593/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006594 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006595 * @ctxt: the XPath Parser context
6596 *
6597 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6598 * | AbbreviatedStep
6599 *
6600 * [12] AbbreviatedStep ::= '.' | '..'
6601 *
6602 * [5] AxisSpecifier ::= AxisName '::'
6603 * | AbbreviatedAxisSpecifier
6604 *
6605 * [13] AbbreviatedAxisSpecifier ::= '@'?
6606 *
6607 * Modified for XPtr range support as:
6608 *
6609 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6610 * | AbbreviatedStep
6611 * | 'range-to' '(' Expr ')' Predicate*
6612 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006613 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006614 * A location step of . is short for self::node(). This is
6615 * particularly useful in conjunction with //. For example, the
6616 * location path .//para is short for
6617 * self::node()/descendant-or-self::node()/child::para
6618 * and so will select all para descendant elements of the context
6619 * node.
6620 * Similarly, a location step of .. is short for parent::node().
6621 * For example, ../title is short for parent::node()/child::title
6622 * and so will select the title children of the parent of the context
6623 * node.
6624 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006625static void
6626xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006627 SKIP_BLANKS;
6628 if ((CUR == '.') && (NXT(1) == '.')) {
6629 SKIP(2);
6630 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006631 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6632 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006633 } else if (CUR == '.') {
6634 NEXT;
6635 SKIP_BLANKS;
6636 } else {
6637 xmlChar *name = NULL;
6638 const xmlChar *prefix = NULL;
6639 xmlXPathTestVal test;
6640 xmlXPathAxisVal axis;
6641 xmlXPathTypeVal type;
6642
6643 /*
6644 * The modification needed for XPointer change to the production
6645 */
6646#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006647 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006648 name = xmlXPathParseNCName(ctxt);
6649 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006650 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006651 xmlFree(name);
6652 SKIP_BLANKS;
6653 if (CUR != '(') {
6654 XP_ERROR(XPATH_EXPR_ERROR);
6655 }
6656 NEXT;
6657 SKIP_BLANKS;
6658
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006659 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006660 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006661 CHECK_ERROR;
6662
6663 SKIP_BLANKS;
6664 if (CUR != ')') {
6665 XP_ERROR(XPATH_EXPR_ERROR);
6666 }
6667 NEXT;
6668 goto eval_predicates;
6669 }
6670 }
6671#endif
6672 if (name == NULL)
6673 name = xmlXPathParseNCName(ctxt);
6674 if (name != NULL) {
6675 axis = xmlXPathIsAxisName(name);
6676 if (axis != 0) {
6677 SKIP_BLANKS;
6678 if ((CUR == ':') && (NXT(1) == ':')) {
6679 SKIP(2);
6680 xmlFree(name);
6681 name = NULL;
6682 } else {
6683 /* an element name can conflict with an axis one :-\ */
6684 axis = AXIS_CHILD;
6685 }
6686 } else {
6687 axis = AXIS_CHILD;
6688 }
6689 } else if (CUR == '@') {
6690 NEXT;
6691 axis = AXIS_ATTRIBUTE;
6692 } else {
6693 axis = AXIS_CHILD;
6694 }
6695
6696 CHECK_ERROR;
6697
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006698 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006699 if (test == 0)
6700 return;
6701
6702#ifdef DEBUG_STEP
6703 xmlGenericError(xmlGenericErrorContext,
6704 "Basis : computing new set\n");
6705#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006706
6707 PUSH_LONG_EXPR(XPATH_OP_COLLECT, axis, test, type,
6708 (void *)prefix, (void *)name);
6709
Owen Taylor3473f882001-02-23 17:55:21 +00006710#ifdef DEBUG_STEP
6711 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6712 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6713#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006714
6715eval_predicates:
6716 SKIP_BLANKS;
6717 while (CUR == '[') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006718 xmlXPathCompPredicate(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006719 }
6720 }
6721#ifdef DEBUG_STEP
6722 xmlGenericError(xmlGenericErrorContext, "Step : ");
6723 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6724 ctxt->value->nodesetval);
6725#endif
6726}
6727
6728/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006729 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006730 * @ctxt: the XPath Parser context
6731 *
6732 * [3] RelativeLocationPath ::= Step
6733 * | RelativeLocationPath '/' Step
6734 * | AbbreviatedRelativeLocationPath
6735 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6736 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006737 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006738 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006739static void
Owen Taylor3473f882001-02-23 17:55:21 +00006740#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006741xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006742#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006743xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006744#endif
6745(xmlXPathParserContextPtr ctxt) {
6746 SKIP_BLANKS;
6747 if ((CUR == '/') && (NXT(1) == '/')) {
6748 SKIP(2);
6749 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006750 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6751 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006752 } else if (CUR == '/') {
6753 NEXT;
6754 SKIP_BLANKS;
6755 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006756 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006757 SKIP_BLANKS;
6758 while (CUR == '/') {
6759 if ((CUR == '/') && (NXT(1) == '/')) {
6760 SKIP(2);
6761 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006762 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006763 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006764 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006765 } else if (CUR == '/') {
6766 NEXT;
6767 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006768 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006769 }
6770 SKIP_BLANKS;
6771 }
6772}
6773
6774/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006775 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006776 * @ctxt: the XPath Parser context
6777 *
6778 * [1] LocationPath ::= RelativeLocationPath
6779 * | AbsoluteLocationPath
6780 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6781 * | AbbreviatedAbsoluteLocationPath
6782 * [10] AbbreviatedAbsoluteLocationPath ::=
6783 * '//' RelativeLocationPath
6784 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006785 * Compile a location path
6786 *
Owen Taylor3473f882001-02-23 17:55:21 +00006787 * // is short for /descendant-or-self::node()/. For example,
6788 * //para is short for /descendant-or-self::node()/child::para and
6789 * so will select any para element in the document (even a para element
6790 * that is a document element will be selected by //para since the
6791 * document element node is a child of the root node); div//para is
6792 * short for div/descendant-or-self::node()/child::para and so will
6793 * select all para descendants of div children.
6794 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006795static void
6796xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006797 SKIP_BLANKS;
6798 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006799 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006800 } else {
6801 while (CUR == '/') {
6802 if ((CUR == '/') && (NXT(1) == '/')) {
6803 SKIP(2);
6804 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006805 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6806 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006807 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006808 } else if (CUR == '/') {
6809 NEXT;
6810 SKIP_BLANKS;
6811 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006812 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006813 }
6814 }
6815 }
6816}
6817
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006818/************************************************************************
6819 * *
6820 * XPath precompiled expression evaluation *
6821 * *
6822 ************************************************************************/
6823
Owen Taylor3473f882001-02-23 17:55:21 +00006824/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006825 * xmlXPathCompOpEval:
6826 * @ctxt: the XPath parser context with the compiled expression
6827 * @op: an XPath compiled operation
6828 *
6829 * Evaluate the Precompiled XPath operation
6830 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006831static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006832xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6833 int equal, ret;
6834 xmlXPathCompExprPtr comp;
6835 xmlXPathObjectPtr arg1, arg2;
6836
6837 comp = ctxt->comp;
6838 switch (op->op) {
6839 case XPATH_OP_END:
6840 return;
6841 case XPATH_OP_AND:
6842 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6843 xmlXPathBooleanFunction(ctxt, 1);
6844 if (ctxt->value->boolval == 0)
6845 return;
6846 arg2 = valuePop(ctxt);
6847 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6848 xmlXPathBooleanFunction(ctxt, 1);
6849 arg1 = valuePop(ctxt);
6850 arg1->boolval &= arg2->boolval;
6851 valuePush(ctxt, arg1);
6852 xmlXPathFreeObject(arg2);
6853 return;
6854 case XPATH_OP_OR:
6855 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6856 xmlXPathBooleanFunction(ctxt, 1);
6857 if (ctxt->value->boolval == 1)
6858 return;
6859 arg2 = valuePop(ctxt);
6860 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6861 xmlXPathBooleanFunction(ctxt, 1);
6862 arg1 = valuePop(ctxt);
6863 arg1->boolval |= arg2->boolval;
6864 valuePush(ctxt, arg1);
6865 xmlXPathFreeObject(arg2);
6866 return;
6867 case XPATH_OP_EQUAL:
6868 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6869 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6870 equal = xmlXPathEqualValues(ctxt);
6871 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6872 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6873 return;
6874 case XPATH_OP_CMP:
6875 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6876 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6877 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6878 valuePush(ctxt, xmlXPathNewBoolean(ret));
6879 return;
6880 case XPATH_OP_PLUS:
6881 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6882 if (op->ch2 != -1)
6883 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6884 if (op->value == 0) xmlXPathSubValues(ctxt);
6885 else if (op->value == 1) xmlXPathAddValues(ctxt);
6886 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6887 else if (op->value == 3) {
6888 xmlXPathObjectPtr arg;
6889
6890 POP_FLOAT
6891 valuePush(ctxt, arg);
6892 }
6893 return;
6894 case XPATH_OP_MULT:
6895 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6896 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6897 if (op->value == 0) xmlXPathMultValues(ctxt);
6898 else if (op->value == 1) xmlXPathDivValues(ctxt);
6899 else if (op->value == 2) xmlXPathModValues(ctxt);
6900 return;
6901 case XPATH_OP_UNION:
6902 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6903 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6904 CHECK_TYPE(XPATH_NODESET);
6905 arg2 = valuePop(ctxt);
6906
6907 CHECK_TYPE(XPATH_NODESET);
6908 arg1 = valuePop(ctxt);
6909
6910 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6911 arg2->nodesetval);
6912 valuePush(ctxt, arg1);
6913 xmlXPathFreeObject(arg2);
6914 return;
6915 case XPATH_OP_ROOT:
6916 xmlXPathRoot(ctxt);
6917 return;
6918 case XPATH_OP_NODE:
6919 if (op->ch1 != -1)
6920 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6921 if (op->ch2 != -1)
6922 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6923 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6924 return;
6925 case XPATH_OP_RESET:
6926 if (op->ch1 != -1)
6927 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6928 if (op->ch2 != -1)
6929 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6930 ctxt->context->node = NULL;
6931 return;
6932 case XPATH_OP_COLLECT:
6933 if (op->ch1 != -1)
6934 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6935 xmlXPathNodeCollectAndTest(ctxt, op->value, op->value2,
6936 op->value3, op->value4, op->value5);
6937 return;
6938 case XPATH_OP_VALUE:
6939 valuePush(ctxt,
6940 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
6941 return;
6942 case XPATH_OP_VARIABLE: {
6943 if (op->ch1 != -1)
6944 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6945 if (op->value5 == NULL)
6946 valuePush(ctxt,
6947 xmlXPathVariableLookup(ctxt->context, op->value4));
6948 else {
6949 const xmlChar *URI;
6950 URI = xmlXPathNsLookup(ctxt->context, op->value5);
6951 if (URI == NULL) {
6952 xmlGenericError(xmlGenericErrorContext,
6953 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
6954 op->value4, op->value5);
6955 return;
6956 }
6957 valuePush(ctxt,
6958 xmlXPathVariableLookupNS(ctxt->context,
6959 op->value4, URI));
6960 }
6961 return;
6962 }
6963 case XPATH_OP_FUNCTION: {
6964 xmlXPathFunction func;
6965
6966 if (op->ch1 != -1)
6967 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6968 if (op->value5 == NULL)
6969 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
6970 else {
6971 const xmlChar *URI;
6972 URI = xmlXPathNsLookup(ctxt->context, op->value5);
6973 if (URI == NULL) {
6974 xmlGenericError(xmlGenericErrorContext,
6975 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
6976 op->value4, op->value5);
6977 return;
6978 }
6979 func = xmlXPathFunctionLookupNS(ctxt->context,
6980 op->value4, URI);
6981 }
6982 if (func == NULL) {
6983 xmlGenericError(xmlGenericErrorContext,
6984 "xmlXPathRunEval: function %s not found\n",
6985 op->value4);
6986 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
6987 return;
6988 }
6989 func(ctxt, op->value);
6990 return;
6991 }
6992 case XPATH_OP_ARG:
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 return;
6998 case XPATH_OP_PREDICATE: {
6999 xmlXPathObjectPtr res;
7000 xmlXPathObjectPtr obj, tmp;
7001 xmlNodeSetPtr newset = NULL;
7002 xmlNodeSetPtr oldset;
7003 xmlNodePtr oldnode;
7004 int i;
7005
7006 if (op->ch1 != -1)
7007 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7008 if (op->ch2 == -1)
7009 return;
7010
7011 oldnode = ctxt->context->node;
7012
7013#ifdef LIBXML_XPTR_ENABLED
7014 /*
7015 * Hum are we filtering the result of an XPointer expression
7016 */
7017 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007018 xmlLocationSetPtr newlocset = NULL;
7019 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007020
7021 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007022 * Extract the old locset, and then evaluate the result of the
7023 * expression for all the element in the locset. use it to grow
7024 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007025 */
7026 CHECK_TYPE(XPATH_LOCATIONSET);
7027 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007028 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007029 ctxt->context->node = NULL;
7030
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007031 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007032 ctxt->context->contextSize = 0;
7033 ctxt->context->proximityPosition = 0;
7034 if (op->ch2 != -1)
7035 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7036 res = valuePop(ctxt);
7037 if (res != NULL)
7038 xmlXPathFreeObject(res);
7039 valuePush(ctxt, obj);
7040 CHECK_ERROR;
7041 return;
7042 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007043 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007044
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007045 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007046 /*
7047 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007048 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007049 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007050 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007051 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7052 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007053 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007054 ctxt->context->proximityPosition = i + 1;
7055
7056 if (op->ch2 != -1)
7057 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7058 CHECK_ERROR;
7059
7060 /*
7061 * The result of the evaluation need to be tested to
7062 * decided whether the filter succeeded or not
7063 */
7064 res = valuePop(ctxt);
7065 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007066 xmlXPtrLocationSetAdd(newlocset,
7067 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007068 }
7069
7070 /*
7071 * Cleanup
7072 */
7073 if (res != NULL)
7074 xmlXPathFreeObject(res);
7075 if (ctxt->value == tmp) {
7076 res = valuePop(ctxt);
7077 xmlXPathFreeObject(res);
7078 }
7079
7080 ctxt->context->node = NULL;
7081 }
7082
7083 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007084 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007085 */
7086 xmlXPathFreeObject(obj);
7087 ctxt->context->node = NULL;
7088 ctxt->context->contextSize = -1;
7089 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007090 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007091 ctxt->context->node = oldnode;
7092 return;
7093 }
7094#endif /* LIBXML_XPTR_ENABLED */
7095
7096 /*
7097 * Extract the old set, and then evaluate the result of the
7098 * expression for all the element in the set. use it to grow
7099 * up a new set.
7100 */
7101 CHECK_TYPE(XPATH_NODESET);
7102 obj = valuePop(ctxt);
7103 oldset = obj->nodesetval;
7104 oldnode = ctxt->context->node;
7105 ctxt->context->node = NULL;
7106
7107 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7108 ctxt->context->contextSize = 0;
7109 ctxt->context->proximityPosition = 0;
7110 if (op->ch2 != -1)
7111 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7112 res = valuePop(ctxt);
7113 if (res != NULL)
7114 xmlXPathFreeObject(res);
7115 valuePush(ctxt, obj);
7116 CHECK_ERROR;
7117 } else {
7118 /*
7119 * Initialize the new set.
7120 */
7121 newset = xmlXPathNodeSetCreate(NULL);
7122
7123 for (i = 0; i < oldset->nodeNr; i++) {
7124 /*
7125 * Run the evaluation with a node list made of
7126 * a single item in the nodeset.
7127 */
7128 ctxt->context->node = oldset->nodeTab[i];
7129 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7130 valuePush(ctxt, tmp);
7131 ctxt->context->contextSize = oldset->nodeNr;
7132 ctxt->context->proximityPosition = i + 1;
7133
7134 if (op->ch2 != -1)
7135 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7136 CHECK_ERROR;
7137
7138 /*
7139 * The result of the evaluation need to be tested to
7140 * decided whether the filter succeeded or not
7141 */
7142 res = valuePop(ctxt);
7143 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7144 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7145 }
7146
7147 /*
7148 * Cleanup
7149 */
7150 if (res != NULL)
7151 xmlXPathFreeObject(res);
7152 if (ctxt->value == tmp) {
7153 res = valuePop(ctxt);
7154 xmlXPathFreeObject(res);
7155 }
7156
7157 ctxt->context->node = NULL;
7158 }
7159
7160 /*
7161 * The result is used as the new evaluation set.
7162 */
7163 xmlXPathFreeObject(obj);
7164 ctxt->context->node = NULL;
7165 ctxt->context->contextSize = -1;
7166 ctxt->context->proximityPosition = -1;
7167 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7168 }
7169 ctxt->context->node = oldnode;
7170 return;
7171 }
7172 case XPATH_OP_SORT:
7173 if (op->ch1 != -1)
7174 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7175 if ((ctxt->value != NULL) &&
7176 (ctxt->value->type == XPATH_NODESET) &&
7177 (ctxt->value->nodesetval != NULL))
7178 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7179 return;
7180#ifdef LIBXML_XPTR_ENABLED
7181 case XPATH_OP_RANGETO: {
7182 xmlXPathObjectPtr range;
7183 xmlXPathObjectPtr res, obj;
7184 xmlXPathObjectPtr tmp;
7185 xmlLocationSetPtr newset = NULL;
7186 xmlNodeSetPtr oldset;
7187 int i;
7188
7189 if (op->ch1 != -1)
7190 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7191 if (op->ch2 == -1)
7192 return;
7193
7194 CHECK_TYPE(XPATH_NODESET);
7195 obj = valuePop(ctxt);
7196 oldset = obj->nodesetval;
7197 ctxt->context->node = NULL;
7198
7199 newset = xmlXPtrLocationSetCreate(NULL);
7200
7201 for (i = 0; i < oldset->nodeNr; i++) {
7202 /*
7203 * Run the evaluation with a node list made of a single item
7204 * in the nodeset.
7205 */
7206 ctxt->context->node = oldset->nodeTab[i];
7207 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7208 valuePush(ctxt, tmp);
7209
7210 if (op->ch2 != -1)
7211 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7212 CHECK_ERROR;
7213
7214 /*
7215 * The result of the evaluation need to be tested to
7216 * decided whether the filter succeeded or not
7217 */
7218 res = valuePop(ctxt);
7219 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7220 if (range != NULL) {
7221 xmlXPtrLocationSetAdd(newset, range);
7222 }
7223
7224 /*
7225 * Cleanup
7226 */
7227 if (res != NULL)
7228 xmlXPathFreeObject(res);
7229 if (ctxt->value == tmp) {
7230 res = valuePop(ctxt);
7231 xmlXPathFreeObject(res);
7232 }
7233
7234 ctxt->context->node = NULL;
7235 }
7236
7237 /*
7238 * The result is used as the new evaluation set.
7239 */
7240 xmlXPathFreeObject(obj);
7241 ctxt->context->node = NULL;
7242 ctxt->context->contextSize = -1;
7243 ctxt->context->proximityPosition = -1;
7244 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7245 return;
7246 }
7247#endif /* LIBXML_XPTR_ENABLED */
7248 }
7249 xmlGenericError(xmlGenericErrorContext,
7250 "XPath: unknown precompiled operation %d\n",
7251 op->op);
7252 return;
7253}
7254
7255/**
7256 * xmlXPathRunEval:
7257 * @ctxt: the XPath parser context with the compiled expression
7258 *
7259 * Evaluate the Precompiled XPath expression in the given context.
7260 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007261static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007262xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7263 xmlXPathCompExprPtr comp;
7264
7265 if ((ctxt == NULL) || (ctxt->comp == NULL))
7266 return;
7267
7268 if (ctxt->valueTab == NULL) {
7269 /* Allocate the value stack */
7270 ctxt->valueTab = (xmlXPathObjectPtr *)
7271 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7272 if (ctxt->valueTab == NULL) {
7273 xmlFree(ctxt);
7274 xmlGenericError(xmlGenericErrorContext,
7275 "xmlXPathRunEval: out of memory\n");
7276 return;
7277 }
7278 ctxt->valueNr = 0;
7279 ctxt->valueMax = 10;
7280 ctxt->value = NULL;
7281 }
7282 comp = ctxt->comp;
7283 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7284}
7285
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007286/************************************************************************
7287 * *
7288 * Public interfaces *
7289 * *
7290 ************************************************************************/
7291
7292/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007293 * xmlXPathEvalPredicate:
7294 * @ctxt: the XPath context
7295 * @res: the Predicate Expression evaluation result
7296 *
7297 * Evaluate a predicate result for the current node.
7298 * A PredicateExpr is evaluated by evaluating the Expr and converting
7299 * the result to a boolean. If the result is a number, the result will
7300 * be converted to true if the number is equal to the position of the
7301 * context node in the context node list (as returned by the position
7302 * function) and will be converted to false otherwise; if the result
7303 * is not a number, then the result will be converted as if by a call
7304 * to the boolean function.
7305 *
7306 * Return 1 if predicate is true, 0 otherwise
7307 */
7308int
7309xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7310 if (res == NULL) return(0);
7311 switch (res->type) {
7312 case XPATH_BOOLEAN:
7313 return(res->boolval);
7314 case XPATH_NUMBER:
7315 return(res->floatval == ctxt->proximityPosition);
7316 case XPATH_NODESET:
7317 case XPATH_XSLT_TREE:
7318 return(res->nodesetval->nodeNr != 0);
7319 case XPATH_STRING:
7320 return((res->stringval != NULL) &&
7321 (xmlStrlen(res->stringval) != 0));
7322 default:
7323 STRANGE
7324 }
7325 return(0);
7326}
7327
7328/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007329 * xmlXPathEvaluatePredicateResult:
7330 * @ctxt: the XPath Parser context
7331 * @res: the Predicate Expression evaluation result
7332 *
7333 * Evaluate a predicate result for the current node.
7334 * A PredicateExpr is evaluated by evaluating the Expr and converting
7335 * the result to a boolean. If the result is a number, the result will
7336 * be converted to true if the number is equal to the position of the
7337 * context node in the context node list (as returned by the position
7338 * function) and will be converted to false otherwise; if the result
7339 * is not a number, then the result will be converted as if by a call
7340 * to the boolean function.
7341 *
7342 * Return 1 if predicate is true, 0 otherwise
7343 */
7344int
7345xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7346 xmlXPathObjectPtr res) {
7347 if (res == NULL) return(0);
7348 switch (res->type) {
7349 case XPATH_BOOLEAN:
7350 return(res->boolval);
7351 case XPATH_NUMBER:
7352 return(res->floatval == ctxt->context->proximityPosition);
7353 case XPATH_NODESET:
7354 case XPATH_XSLT_TREE:
7355 return(res->nodesetval->nodeNr != 0);
7356 case XPATH_STRING:
7357 return((res->stringval != NULL) &&
7358 (xmlStrlen(res->stringval) != 0));
7359 default:
7360 STRANGE
7361 }
7362 return(0);
7363}
7364
7365/**
7366 * xmlXPathCompile:
7367 * @str: the XPath expression
7368 *
7369 * Compile an XPath expression
7370 *
7371 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7372 * the caller has to free the object.
7373 */
7374xmlXPathCompExprPtr
7375xmlXPathCompile(const xmlChar *str) {
7376 xmlXPathParserContextPtr ctxt;
7377 xmlXPathCompExprPtr comp;
7378
7379 xmlXPathInit();
7380
7381 ctxt = xmlXPathNewParserContext(str, NULL);
7382 xmlXPathCompileExpr(ctxt);
7383
7384 comp = ctxt->comp;
7385 ctxt->comp = NULL;
7386 xmlXPathFreeParserContext(ctxt);
7387 return(comp);
7388}
7389
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007390/**
7391 * xmlXPathCompiledEval:
7392 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007393 * @ctx: the XPath context
7394 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007395 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007396 *
7397 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7398 * the caller has to free the object.
7399 */
7400xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007401xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007402 xmlXPathParserContextPtr ctxt;
7403 xmlXPathObjectPtr res, tmp, init = NULL;
7404 int stack = 0;
7405
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007406 if ((comp == NULL) || (ctx == NULL))
7407 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007408 xmlXPathInit();
7409
7410 CHECK_CONTEXT(ctx)
7411
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007412 ctxt = xmlXPathCompParserContext(comp, ctx);
7413 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007414
7415 if (ctxt->value == NULL) {
7416 xmlGenericError(xmlGenericErrorContext,
7417 "xmlXPathEval: evaluation failed\n");
7418 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007419 } else {
7420 res = valuePop(ctxt);
7421 }
7422
7423 do {
7424 tmp = valuePop(ctxt);
7425 if (tmp != NULL) {
7426 if (tmp != init)
7427 stack++;
7428 xmlXPathFreeObject(tmp);
7429 }
7430 } while (tmp != NULL);
7431 if ((stack != 0) && (res != NULL)) {
7432 xmlGenericError(xmlGenericErrorContext,
7433 "xmlXPathEval: %d object left on the stack\n",
7434 stack);
7435 }
7436 if (ctxt->error != XPATH_EXPRESSION_OK) {
7437 xmlXPathFreeObject(res);
7438 res = NULL;
7439 }
7440
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007441
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007442 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007443 xmlXPathFreeParserContext(ctxt);
7444 return(res);
7445}
7446
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007447/**
7448 * xmlXPathEvalExpr:
7449 * @ctxt: the XPath Parser context
7450 *
7451 * Parse and evaluate an XPath expression in the given context,
7452 * then push the result on the context stack
7453 */
7454void
7455xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7456 xmlXPathCompileExpr(ctxt);
7457 xmlXPathRunEval(ctxt);
7458}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007459
7460/**
7461 * xmlXPathEval:
7462 * @str: the XPath expression
7463 * @ctx: the XPath context
7464 *
7465 * Evaluate the XPath Location Path in the given context.
7466 *
7467 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7468 * the caller has to free the object.
7469 */
7470xmlXPathObjectPtr
7471xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7472 xmlXPathParserContextPtr ctxt;
7473 xmlXPathObjectPtr res, tmp, init = NULL;
7474 int stack = 0;
7475
7476 xmlXPathInit();
7477
7478 CHECK_CONTEXT(ctx)
7479
7480 ctxt = xmlXPathNewParserContext(str, ctx);
7481 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007482
7483 if (ctxt->value == NULL) {
7484 xmlGenericError(xmlGenericErrorContext,
7485 "xmlXPathEval: evaluation failed\n");
7486 res = NULL;
7487 } else if (*ctxt->cur != 0) {
7488 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7489 res = NULL;
7490 } else {
7491 res = valuePop(ctxt);
7492 }
7493
7494 do {
7495 tmp = valuePop(ctxt);
7496 if (tmp != NULL) {
7497 if (tmp != init)
7498 stack++;
7499 xmlXPathFreeObject(tmp);
7500 }
7501 } while (tmp != NULL);
7502 if ((stack != 0) && (res != NULL)) {
7503 xmlGenericError(xmlGenericErrorContext,
7504 "xmlXPathEval: %d object left on the stack\n",
7505 stack);
7506 }
7507 if (ctxt->error != XPATH_EXPRESSION_OK) {
7508 xmlXPathFreeObject(res);
7509 res = NULL;
7510 }
7511
Owen Taylor3473f882001-02-23 17:55:21 +00007512 xmlXPathFreeParserContext(ctxt);
7513 return(res);
7514}
7515
7516/**
7517 * xmlXPathEvalExpression:
7518 * @str: the XPath expression
7519 * @ctxt: the XPath context
7520 *
7521 * Evaluate the XPath expression in the given context.
7522 *
7523 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7524 * the caller has to free the object.
7525 */
7526xmlXPathObjectPtr
7527xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7528 xmlXPathParserContextPtr pctxt;
7529 xmlXPathObjectPtr res, tmp;
7530 int stack = 0;
7531
7532 xmlXPathInit();
7533
7534 CHECK_CONTEXT(ctxt)
7535
7536 pctxt = xmlXPathNewParserContext(str, ctxt);
7537 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007538
7539 if (*pctxt->cur != 0) {
7540 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7541 res = NULL;
7542 } else {
7543 res = valuePop(pctxt);
7544 }
7545 do {
7546 tmp = valuePop(pctxt);
7547 if (tmp != NULL) {
7548 xmlXPathFreeObject(tmp);
7549 stack++;
7550 }
7551 } while (tmp != NULL);
7552 if ((stack != 0) && (res != NULL)) {
7553 xmlGenericError(xmlGenericErrorContext,
7554 "xmlXPathEvalExpression: %d object left on the stack\n",
7555 stack);
7556 }
7557 xmlXPathFreeParserContext(pctxt);
7558 return(res);
7559}
7560
7561/**
7562 * xmlXPathRegisterAllFunctions:
7563 * @ctxt: the XPath context
7564 *
7565 * Registers all default XPath functions in this context
7566 */
7567void
7568xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7569{
7570 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7571 xmlXPathBooleanFunction);
7572 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7573 xmlXPathCeilingFunction);
7574 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7575 xmlXPathCountFunction);
7576 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7577 xmlXPathConcatFunction);
7578 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7579 xmlXPathContainsFunction);
7580 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7581 xmlXPathIdFunction);
7582 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7583 xmlXPathFalseFunction);
7584 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7585 xmlXPathFloorFunction);
7586 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7587 xmlXPathLastFunction);
7588 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7589 xmlXPathLangFunction);
7590 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7591 xmlXPathLocalNameFunction);
7592 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7593 xmlXPathNotFunction);
7594 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7595 xmlXPathNameFunction);
7596 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7597 xmlXPathNamespaceURIFunction);
7598 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7599 xmlXPathNormalizeFunction);
7600 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7601 xmlXPathNumberFunction);
7602 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7603 xmlXPathPositionFunction);
7604 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7605 xmlXPathRoundFunction);
7606 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7607 xmlXPathStringFunction);
7608 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7609 xmlXPathStringLengthFunction);
7610 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7611 xmlXPathStartsWithFunction);
7612 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7613 xmlXPathSubstringFunction);
7614 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7615 xmlXPathSubstringBeforeFunction);
7616 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7617 xmlXPathSubstringAfterFunction);
7618 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7619 xmlXPathSumFunction);
7620 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7621 xmlXPathTrueFunction);
7622 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7623 xmlXPathTranslateFunction);
7624}
7625
7626#endif /* LIBXML_XPATH_ENABLED */